{"thread":{"id":"50690","subject":"[PATCH v1 00/11] And new command \"restore\"","startedAt":"2019-03-08T10:17:07Z","lastAt":"2019-06-13T15:19:30Z","messageCount":97,"participants":["Nguyễn Thái Ngọc Duy","Elijah Newren","Duy Nguyen","Andrei Rybak","Eric Sunshine","Jacob Keller","Randall S. Becker","Junio C Hamano","Johannes Schindelin","Emily Shaffer","SZEDER Gábor"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"370972","messageId":"20190308101655.9767-1-pclouds@gmail.com","threadId":"50690","inReplyTo":null,"subject":"[PATCH v1 00/11] And new command \"restore\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:44Z","receivedAt":"2019-03-08T10:17:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the companion of \"git switch\" [1] and is based on that topic.\nThis command peforms the \"checkout paths\" from git-checkout, git-reset\nand also has a third mode to reset only worktree, leaving the index\nalone.\n\nFor new people not aware of previous discussions, this command is\nsupposed to be a friendlier replacement for \"git checkout\" and\nhopefully fixes some warts that have grown over the time in git-checkout.\nFor this reason, the last patch starts to recommend \"git restore\"\neverywhere.\n\nFor old people, I'm surprised nobody reacts to the command rename from\nrestore-files to restore. \"restore\" vs \"reset\" is certainly confusing.\nMy hope was to remove \"common porcelain\" status from reset, but I\nprobably will not succeed in doing that.\n\nSome open issues from the last discussion [2]\n\n- intent-to-add support. This will come but maybe later.\n\n- --index has a different meaning in git-apply. I don't have a good\n  suggestion except \"yeah we have two different worlds now, the old\n  and the new one\"\n\nThe series is also available at [3]\n\n[1] https://public-inbox.org/git/20190308095752.8574-1-pclouds@gmail.com\n[2] https://public-inbox.org/git/CACsJy8CQhWeC3b6eGPePuRejfOx7c17X61-wqq5kOiRzYkRESw@mail.gmail.com/\n[3] https://gitlab.com/pclouds/git/tree/switch-and-restore\n\nNguyễn Thái Ngọc Duy (11):\n  checkout: split part of it to new command 'restore'\n  restore: take tree-ish from --source option instead\n  restore: make pathspec mandatory\n  restore: disable overlay mode by default\n  checkout: factor out worktree checkout code\n  restore: add --worktree and --index\n  restore: default to --source=HEAD when only --index is specified\n  restore: support --patch\n  t: add tests for restore\n  completion: support restore\n  doc: promote \"git restore\"\n\n .gitignore                             |   1 +\n Documentation/config/interactive.txt   |   3 +-\n Documentation/git-checkout.txt         |   1 +\n Documentation/git-clean.txt            |   2 +-\n Documentation/git-commit.txt           |   4 +-\n Documentation/git-format-patch.txt     |   2 +-\n Documentation/git-reset.txt            |   6 +-\n Documentation/git-restore.txt (new)    | 172 ++++++++++++++++\n Documentation/git-revert.txt           |   4 +-\n Documentation/gitcli.txt               |   4 +-\n Documentation/giteveryday.txt          |   4 +-\n Documentation/gittutorial-2.txt        |   4 +-\n Documentation/gittutorial.txt          |   2 +-\n Documentation/user-manual.txt          |  14 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/checkout.c                     | 259 +++++++++++++++++++------\n builtin/clone.c                        |   2 +-\n builtin/commit.c                       |   2 +-\n command-list.txt                       |   3 +-\n contrib/completion/git-completion.bash |  15 ++\n git-add--interactive.perl              |  52 +++++\n git.c                                  |   1 +\n t/lib-patch-mode.sh                    |  12 ++\n t/t2070-restore.sh (new +x)            |  77 ++++++++\n t/t2071-restore-patch.sh (new +x)      | 105 ++++++++++\n t/t7508-status.sh                      |  82 ++++----\n t/t7512-status-help.sh                 |  20 +-\n wt-status.c                            |  26 ++-\n 29 files changed, 736 insertions(+), 145 deletions(-)\n create mode 100644 Documentation/git-restore.txt\n create mode 100755 t/t2070-restore.sh\n create mode 100755 t/t2071-restore-patch.sh\n\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370973","messageId":"20190308101655.9767-2-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:45Z","receivedAt":"2019-03-08T10:17:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Previously the switching branch business of 'git checkout' becomes a\nnew command. This adds restore command for the checking out\npaths path.\n\nSimilar to switch, a new man page is added to describe what the\ncommand will become. The implementation will be updated shortly to\nmatch the man page.\n\nA couple of differences from 'git checkout' worth highlighting:\n\n- 'restore' by default will only update worktree. This matters more\n  when --source is specified ('checkout <tree> <paths>' updates both\n  worktree and index).\n\n- 'restore --index' can be used to restore the index. This command\n  overlaps with 'git reset <paths>'.\n\n- both worktree and index could also be restored at the same time\n  (from a tree). This overlaps with 'git checkout <tree> <paths>'\n\n- default source for restoring worktree and index is the index and\n  HEAD respectively. A different (tree) source could be specified as\n  with --source (*).\n\n- when both index and worktree are restored, --source must be\n  specified since the default source for these two individual targets\n  are different (**)\n\n- --no-overlay is enabled by default, if an entry is missing in the\n  source, restoring means deleting the entry\n\n(*) I originally went with --from instead of --source. I still think\n  --from is a better name. The short option -f however is already\n  taken by force. And I do think short option is good to have, e.g. to\n  write -s@ or -s@^ instead of --source=HEAD.\n\n(**) If you sit down and think about it, moving worktree's source from\n  the index to HEAD makes sense, but nobody is really thinking it\n  through when they type the commands.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                           |   1 +\n Documentation/config/interactive.txt |   3 +-\n Documentation/git-checkout.txt       |   1 +\n Documentation/git-restore.txt (new)  | 172 +++++++++++++++++++++++++++\n Makefile                             |   1 +\n builtin.h                            |   1 +\n builtin/checkout.c                   |  26 ++++\n command-list.txt                     |   1 +\n git.c                                |   1 +\n 9 files changed, 206 insertions(+), 1 deletion(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex c687b92b1c..fb377106be 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -143,6 +143,7 @@\n /git-request-pull\n /git-rerere\n /git-reset\n+/git-restore\n /git-rev-list\n /git-rev-parse\n /git-revert\ndiff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt\nindex ad846dd7c9..a2d3c7ec44 100644\n--- a/Documentation/config/interactive.txt\n+++ b/Documentation/config/interactive.txt\n@@ -2,7 +2,8 @@ interactive.singleKey::\n \tIn interactive commands, allow the user to provide one-letter\n \tinput with a single key (i.e., without hitting enter).\n \tCurrently this is used by the `--patch` mode of\n-\tlinkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],\n+\tlinkgit:git-add[1], linkgit:git-checkout[1],\n+\tlinkgit:git-restore[1], linkgit:git-commit[1],\n \tlinkgit:git-reset[1], and linkgit:git-stash[1]. Note that this\n \tsetting is silently ignored if portable keystroke input\n \tis not available; requires the Perl module Term::ReadKey.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 2b776c1269..e107099c8c 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -571,6 +571,7 @@ $ git add frotz\n SEE ALSO\n --------\n linkgit:git-switch[1]\n+linkgit:git-restore[1]\n \n GIT\n ---\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nnew file mode 100644\nindex 0000000000..a667a5ced4\n--- /dev/null\n+++ b/Documentation/git-restore.txt\n@@ -0,0 +1,172 @@\n+git-restore(1)\n+==============\n+\n+NAME\n+----\n+git-restore - Restore working tree files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git restore' [<options>] [--source=<revision>] <pathspec>...\n+'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+Restore paths in the working tree by replacing with the contents in\n+the restore source or remove if the paths do not exist in the restore\n+source. The source is by default the index but could be from a commit.\n+The command can also optionally restore content in the index from a\n+commit.\n+\n+When a `<revision>` is given, the paths that match the `<pathspec>` are\n+updated both in the index and in the working tree.\n+\n+OPTIONS\n+-------\n+-s<tree>::\n+--source=<tree>::\n+\tRestore the working tree files with the content from the given\n+\ttree or any revision that leads to a tree (e.g. a commit or a\n+\tbranch).\n+\n+-p::\n+--patch::\n+\tInteractively select hunks in the difference between the\n+\t`<revision>` (or the index, if unspecified) and the working\n+\ttree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n+\tto learn how to operate the `--patch` mode.\n+\n+-W::\n+--worktree::\n+-I::\n+--index::\n+\tSpecify the restore location. If neither option is specified,\n+\tby default the working tree is restored. If `--index` is\n+\tspecified without `--worktree` or `--source`, `--source=HEAD`\n+\tis implied. These options only make sense to use with\n+\t`--source`.\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-f::\n+--force::\n+\tIf `--source` is not specified, unmerged entries are left alone\n+\tand will not fail the operation. Unmerged entries are always\n+\treplaced if `--source` is specified, regardless of `--force`.\n+\n+--ours::\n+--theirs::\n+\tCheck out stage #2 ('ours') or #3 ('theirs') for unmerged\n+\tpaths.\n++\n+Note that during `git rebase` and `git pull --rebase`, 'ours' and\n+'theirs' may appear swapped. See the explanation of the same options\n+in linkgit:git-checkout[1] for details.\n+\n+-m::\n+--merge::\n+\tRecreate the conflicted merge in the specified paths.\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the merge.conflictStyle\n+\tconfiguration variable.  Possible values are \"merge\" (default)\n+\tand \"diff3\" (in addition to what is shown by \"merge\" style,\n+\tshows the original contents).\n+\n+--ignore-skip-worktree-bits::\n+\tIn sparse checkout mode, by default update only entries\n+\tmatched by `<pathspec>` and sparse patterns in\n+\t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n+\tpatterns and unconditionally restores any files in `<pathspec>`.\n+\n+--recurse-submodules::\n+--no-recurse-submodules::\n+\tUsing `--recurse-submodules` will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+--overlay::\n+--no-overlay::\n+\tIn overlay mode, `git checkout` never removes files from the\n+\tindex or the working tree. In no-overlay mode, files that\n+\tappear in the index and working tree, but not in `--source` tree\n+\tare removed, to make them match `<tree-ish>` exactly. The\n+\tdefault is no-overlay mode.\n+\n+EXAMPLES\n+--------\n+\n+The following sequence checks out the `master` branch, reverts\n+the `Makefile` to two revisions back, deletes hello.c by\n+mistake, and gets it back from the index.\n+\n+------------\n+$ git switch master\n+$ git restore --source master~2 Makefile  <1>\n+$ rm -f hello.c\n+$ git restore hello.c                   <2>\n+------------\n+\n+<1> take a file out of another commit\n+<2> restore hello.c from the index\n+\n+If you want to check out _all_ C source files out of the index,\n+you can say\n+\n+------------\n+$ git restore '*.c'\n+------------\n+\n+Note the quotes around `*.c`.  The file `hello.c` will also be\n+checked out, even though it is no longer in the working tree,\n+because the file globbing is used to match entries in the index\n+(not in the working tree by the shell).\n+\n+To restore all files in the current directory\n+\n+------------\n+$ git restore .\n+------------\n+\n+or to restore all working tree files with 'top' pathspec magic (see\n+linkgit::gitglossary[7])\n+\n+------------\n+$ git restore :/\n+------------\n+\n+To restore a file in the index only (this is the same as using\n+\"git reset\")\n+\n+------------\n+$ git restore --index hello.c\n+------------\n+\n+or you can restore both the index and the working tree\n+\n+------------\n+$ git restore --source=HEAD --index --worktree hello.c\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex 8e91db73ad..ffe7e4f58f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n BUILT_INS += git-merge-subtree$X\n+BUILT_INS += git-restore$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\ndiff --git a/builtin.h b/builtin.h\nindex c64e44450e..6830000e14 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repack(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n+extern int cmd_restore(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4903359b49..11dd2ae44c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -37,6 +37,11 @@ static const char * const switch_branch_usage[] = {\n \tNULL,\n };\n \n+static const char * const restore_files_usage[] = {\n+\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1528,3 +1533,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tFREE_AND_NULL(options);\n \treturn ret;\n }\n+\n+int cmd_restore(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_pathspec = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, restore_files_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 13317f47d4..b9eae1c258 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n+git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 39582cf511..6d439e723f 100644\n--- a/git.c\n+++ b/git.c\n@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370974","messageId":"20190308101655.9767-3-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 02/11] restore: take tree-ish from --source option instead","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:46Z","receivedAt":"2019-03-08T10:17:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is another departure from 'git checkout' syntax, which uses -- to\nseparate ref and pathspec. The observation is restore (or \"git\ncheckout ,, <pathspec>\") is most often used to restore some files from\nthe index. If this is correct, we can simplify it by taking a way the\nref, so that we can write\n\n    git restore some-file\n\nwithout worrying about some-file being a ref and whether we need to do\n\n    git restore -- some-file\n\nfor safety. If the source of the restore comes from a tree, it will be\nin the form of an option with value, e.g.\n\n    git restore --source=this-tree some-file\n\nThis is of course longer to type than using \"--\". But hopefully it\nwill not be used as often, and it is clearly easier to understand.\n\ndwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\nbecause it's irrelevant because we don't really care about dwim-ing.\nWith accept_ref being unset, dwim can't happen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------\n 1 file changed, 34 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 11dd2ae44c..838343d6aa 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {\n };\n \n static const char * const restore_files_usage[] = {\n-\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tN_(\"git restore [<options>] [--source=<branch>] <file>...\"),\n \tNULL,\n };\n \n@@ -57,6 +57,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint dwim_new_local_branch;\n+\tint accept_ref;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n@@ -72,6 +73,7 @@ struct checkout_opts {\n \tint branch_exists;\n \tconst char *prefix;\n \tstruct pathspec pathspec;\n+\tconst char *from_treeish;\n \tstruct tree *source_tree;\n };\n \n@@ -1324,6 +1326,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n+\tint parseopt_flags = 0;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1335,8 +1338,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\targc = parse_options(argc, argv, prefix, options, usagestr,\n-\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\tif (!opts->accept_pathspec && !opts->accept_ref)\n+\t\tBUG(\"make up your mind, you need to take _something_\");\n+\tif (opts->accept_pathspec && opts->accept_ref)\n+\t\tparseopt_flags = PARSE_OPT_KEEP_DASHDASH;\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     usagestr, parseopt_flags);\n \n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n@@ -1393,7 +1401,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t * including \"last branch\" syntax and DWIM-ery for names of\n \t * remote branches, erroring out for invalid or ambiguous cases.\n \t */\n-\tif (argc) {\n+\tif (argc && opts->accept_ref) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n@@ -1405,6 +1413,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n+\t} else if (!opts->accept_ref && opts->from_treeish) {\n+\t\tstruct object_id rev;\n+\n+\t\tif (get_oid_mb(opts->from_treeish, &rev))\n+\t\t\tdie(_(\"could not resolve %s\"), opts->from_treeish);\n+\n+\t\tsetup_new_branch_info_and_source_tree(&new_branch_info,\n+\t\t\t\t\t\t      opts, &rev,\n+\t\t\t\t\t\t      opts->from_treeish);\n+\n+\t\tif (!opts->source_tree)\n+\t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n \tif (argc) {\n@@ -1488,6 +1508,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.only_merge_on_switching_branches = 0;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1519,6 +1540,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 0;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n@@ -1537,15 +1559,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n int cmd_restore(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option restore_options[] = {\n+\t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n+\t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.dwim_new_local_branch = 1;\n-\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370975","messageId":"20190308101655.9767-4-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 03/11] restore: make pathspec mandatory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:47Z","receivedAt":"2019-03-08T10:17:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore\" without arguments does not make much sense when\nit's about restoring files (what files now?). We could default to\neither\n\n    git restore .\n\nor\n\n    git restore :/\n\nNeither is intuitive. Make the user always give pathspec, force the\nuser to think the scope of restore they want because this is a\ndestructive operation.\n\n\"git restore -p\" without pathspec is an exception to this\nbecause it really is a separate mode. It will be treated as running\npatch mode on the whole worktree.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 838343d6aa..c52ce13d2a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -61,6 +61,7 @@ struct checkout_opts {\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n+\tint empty_pathspec_ok;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1427,6 +1428,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\tif (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&\n+\t    !opts->patch_mode)\t/* patch mode is special */\n+\t\tdie(_(\"pathspec is required\"));\n+\n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n \t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n@@ -1511,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n+\topts.empty_pathspec_ok = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1570,6 +1576,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n+\topts.empty_pathspec_ok = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370976","messageId":"20190308101655.9767-5-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 04/11] restore: disable overlay mode by default","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:48Z","receivedAt":"2019-03-08T10:17:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Overlay mode is considered confusing when the command is about\nrestoring files on worktree. Disable it by default. The user can still\nturn it on, or use 'git checkout' which still has overlay mode on by\ndefault.\n\nWhile at there make the check in checkout_branch() stricter. Neither\n--overlay or --no-overlay should be accepted in branch switching mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c52ce13d2a..9e59bf792f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1196,9 +1196,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n \t\t    \"--patch\");\n \n-\tif (!opts->overlay_mode)\n+\tif (opts->overlay_mode != -1)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n-\t\t    \"--no-overlay\");\n+\t\t    \"--[no]-overlay\");\n \n \tif (opts->writeout_stage)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n@@ -1313,7 +1313,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n@@ -1333,7 +1332,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n-\topts->overlay_mode = -1;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1505,6 +1503,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n \t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1517,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.empty_pathspec_ok = 1;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1551,6 +1551,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n@@ -1569,6 +1570,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1577,6 +1579,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n+\topts.overlay_mode = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370977","messageId":"20190308101655.9767-6-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 05/11] checkout: factor out worktree checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:49Z","receivedAt":"2019-03-08T10:17:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------\n 1 file changed, 59 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9e59bf792f..5fb85e7b73 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -326,17 +326,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,\n \t}\n }\n \n+static int checkout_worktree(const struct checkout_opts *opts)\n+{\n+\tstruct checkout state = CHECKOUT_INIT;\n+\tint nr_checkouts = 0, nr_unmerged = 0;\n+\tint errs = 0;\n+\tint pos;\n+\n+\tstate.force = 1;\n+\tstate.refresh_cache = 1;\n+\tstate.istate = &the_index;\n+\n+\tenable_delayed_checkout(&state);\n+\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\tif (ce->ce_flags & CE_MATCHED) {\n+\t\t\tif (!ce_stage(ce)) {\n+\t\t\t\terrs |= checkout_entry(ce, &state,\n+\t\t\t\t\t\t       NULL, &nr_checkouts);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (opts->writeout_stage)\n+\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n+\t\t\t\t\t\t       ce, pos,\n+\t\t\t\t\t\t       &state,\n+\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n+\t\t\telse if (opts->merge)\n+\t\t\t\terrs |= checkout_merged(pos, &state,\n+\t\t\t\t\t\t\t&nr_unmerged);\n+\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t}\n+\t}\n+\tremove_marked_cache_entries(&the_index, 1);\n+\tremove_scheduled_dirs();\n+\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n+\n+\tif (opts->count_checkout_paths) {\n+\t\tif (nr_unmerged)\n+\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n+\t\t\t\t\t      \"Recreated %d merge conflicts\",\n+\t\t\t\t\t      nr_unmerged),\n+\t\t\t\t   nr_unmerged);\n+\t\tif (opts->source_tree)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n+\t\t\t\t\t      \"Updated %d paths from %s\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts,\n+\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n+\t\t\t\t\t\t      DEFAULT_ABBREV));\n+\t\telse if (!nr_unmerged || nr_checkouts)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n+\t\t\t\t\t      \"Updated %d paths from the index\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts);\n+\t}\n+\n+\treturn errs;\n+}\n+\n static int checkout_paths(const struct checkout_opts *opts,\n \t\t\t  const char *revision)\n {\n \tint pos;\n-\tstruct checkout state = CHECKOUT_INIT;\n \tstatic char *ps_matched;\n \tstruct object_id rev;\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n-\tint nr_checkouts = 0, nr_unmerged = 0;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -422,53 +478,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\tstate.force = 1;\n-\tstate.refresh_cache = 1;\n-\tstate.istate = &the_index;\n-\n-\tenable_delayed_checkout(&state);\n-\tfor (pos = 0; pos < active_nr; pos++) {\n-\t\tstruct cache_entry *ce = active_cache[pos];\n-\t\tif (ce->ce_flags & CE_MATCHED) {\n-\t\t\tif (!ce_stage(ce)) {\n-\t\t\t\terrs |= checkout_entry(ce, &state,\n-\t\t\t\t\t\t       NULL, &nr_checkouts);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (opts->writeout_stage)\n-\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n-\t\t\t\t\t\t       ce, pos,\n-\t\t\t\t\t\t       &state,\n-\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n-\t\t\telse if (opts->merge)\n-\t\t\t\terrs |= checkout_merged(pos, &state,\n-\t\t\t\t\t\t\t&nr_unmerged);\n-\t\t\tpos = skip_same_name(ce, pos) - 1;\n-\t\t}\n-\t}\n-\tremove_marked_cache_entries(&the_index, 1);\n-\tremove_scheduled_dirs();\n-\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n-\n-\tif (opts->count_checkout_paths) {\n-\t\tif (nr_unmerged)\n-\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n-\t\t\t\t\t      \"Recreated %d merge conflicts\",\n-\t\t\t\t\t      nr_unmerged),\n-\t\t\t\t   nr_unmerged);\n-\t\tif (opts->source_tree)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n-\t\t\t\t\t      \"Updated %d paths from %s\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts,\n-\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n-\t\t\t\t\t\t      DEFAULT_ABBREV));\n-\t\telse if (!nr_unmerged || nr_checkouts)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n-\t\t\t\t\t      \"Updated %d paths from the index\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts);\n-\t}\n+\terrs |= checkout_worktree(opts);\n \n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370978","messageId":"20190308101655.9767-7-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 06/11] restore: add --worktree and --index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:50Z","receivedAt":"2019-03-08T10:17:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"'git checkout <tree-ish> <pathspec>' updates both index and\nworktree. But updating the index when you want to restore worktree\nfiles is non-intuitive. The index contains the data ready for the next\ncommit, and there's no indication that the user will want to commit\nthe restored versions.\n\n'git restore' therefore by default only touches worktree. The user has\nthe option to update either the index with\n\n    git restore --source=<tree> --index <path>  (1)\n\nor update both with\n\n    git restore --source=<tree> --index --worktree <path> (2)\n\nPS. Orignally I wanted to make worktree update default and form (1)\nwould add index update while also updating the worktree, and the user\nwould need to do \"--index --no-worktree\" to update index only. But it\nlooks really confusing that \"--index\" option alone updates both. So\nnow form (2) is used for both, which reads much more obvious.\n\nPPS. Yes form (1) overlaps with \"git reset <rev> <path>\". I don't know\nif we can ever turn \"git reset\" back to \"_always_ reset HEAD and\noptionally do something else\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 68 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5fb85e7b73..07b431be51 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -62,6 +62,8 @@ struct checkout_opts {\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n \tint empty_pathspec_ok;\n+\tint checkout_index;\n+\tint checkout_worktree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -393,6 +395,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tint checkout_index;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -418,9 +421,26 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n \t\t    opts->new_branch);\n \n-\tif (opts->patch_mode)\n-\t\treturn run_add_interactive(revision, \"--patch=checkout\",\n-\t\t\t\t\t   &opts->pathspec);\n+\tif (!opts->checkout_worktree && !opts->checkout_index)\n+\t\tdie(_(\"neither '%s' or '%s' is specified\"),\n+\t\t    \"--index\", \"--worktree\");\n+\n+\tif (!opts->source_tree && !opts->checkout_worktree)\n+\t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n+\t\t    \"--worktree\", \"--source\");\n+\n+\tif (opts->patch_mode) {\n+\t\tconst char *patch_mode;\n+\n+\t\tif (opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=checkout\";\n+\t\telse if (opts->checkout_index && !opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse\n+\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n+\t\t\t    \"--patch\", \"--worktree\");\n+\t\treturn run_add_interactive(revision, patch_mode, &opts->pathspec);\n+\t}\n \n \trepo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(&opts->pathspec) < 0)\n@@ -478,10 +498,30 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\terrs |= checkout_worktree(opts);\n+\tif (opts->checkout_worktree)\n+\t\terrs |= checkout_worktree(opts);\n \n-\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n-\t\tdie(_(\"unable to write new index file\"));\n+\t/*\n+\t * Allow updating the index when checking out from the index.\n+\t * This is to save new stat info.\n+\t */\n+\tif (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)\n+\t\tcheckout_index = 1;\n+\telse\n+\t\tcheckout_index = opts->checkout_index;\n+\n+\tif (checkout_index) {\n+\t\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n+\t\t\tdie(_(\"unable to write new index file\"));\n+\t} else {\n+\t\t/*\n+\t\t * NEEDSWORK: if --worktree is not specified, we\n+\t\t * should save stat info of checked out files in the\n+\t\t * index to avoid the next (potentially costly)\n+\t\t * refresh. But it's a bit tricker to do...\n+\t\t */\n+\t\trollback_lock_file(&lock_file);\n+\t}\n \n \tread_ref_full(\"HEAD\", 0, &rev, NULL);\n \thead = lookup_commit_reference_gently(the_repository, &rev, 1);\n@@ -1373,6 +1413,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n+\tif (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = 0;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = 0;\n+\t} else {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = -opts->checkout_index - 1;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = -opts->checkout_worktree - 1;\n+\t}\n+\tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n+\t\tBUG(\"these flags should be non-negative by now\");\n+\n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n@@ -1527,6 +1581,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.implicit_detach = 1;\n \topts.empty_pathspec_ok = 1;\n \topts.overlay_mode = -1;\n+\topts.checkout_index = -2;    /* default on */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1580,6 +1636,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL('I', \"index\", &opts.checkout_index,\n+\t\t\t   N_(\"restore the index\")),\n+\t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n+\t\t\t   N_(\"restore the working tree (default)\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1590,6 +1650,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n \topts.overlay_mode = 0;\n+\topts.checkout_index = -1;    /* default off */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370979","messageId":"20190308101655.9767-8-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:51Z","receivedAt":"2019-03-08T10:17:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore --index\" does not make much sense since we're told to\nrestore the index from the (by default) index. Set default source to\nHEAD in this case. This is basically the same as \"git reset -- <paths>\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 07b431be51..f06439dbeb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1426,6 +1426,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t}\n \tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n \t\tBUG(\"these flags should be non-negative by now\");\n+\t/*\n+\t * convenient shortcut: \"git restore --index\" equals\n+\t * \"git restore --index --source HEAD\"\n+\t */\n+\tif (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)\n+\t\topts->from_treeish = \"HEAD\";\n \n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370980","messageId":"20190308101655.9767-9-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 08/11] restore: support --patch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:52Z","receivedAt":"2019-03-08T10:17:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-restore is different from git-checkout that it only restores the\nworktree by default, not both worktree and index. add--interactive\nneeds some update to support this mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c        |  5 ++--\n git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 55 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f06439dbeb..73de110d42 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -436,9 +436,10 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\tpatch_mode = \"--patch=checkout\";\n \t\telse if (opts->checkout_index && !opts->checkout_worktree)\n \t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse if (!opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=worktree\";\n \t\telse\n-\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n-\t\t\t    \"--patch\", \"--worktree\");\n+\t\t\tBUG(\"either flag must have been set\");\n \t\treturn run_add_interactive(revision, patch_mode, &opts->pathspec);\n \t}\n \ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..3dfb3629c9 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -149,6 +149,20 @@ sub colored {\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@@ -1049,6 +1063,12 @@ sub color_diff {\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@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {\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@@ -1421,6 +1453,16 @@ sub display_hunks {\n \t\tdeletion => N__(\"Apply deletion 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\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\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n );\n \n sub patch_update_file {\n@@ -1756,6 +1798,16 @@ sub process_args {\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-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370981","messageId":"20190308101655.9767-10-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 09/11] t: add tests for restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:53Z","receivedAt":"2019-03-08T10:17:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/lib-patch-mode.sh               |  12 ++++\n t/t2070-restore.sh (new +x)       |  77 ++++++++++++++++++++++\n t/t2071-restore-patch.sh (new +x) | 105 ++++++++++++++++++++++++++++++\n 3 files changed, 194 insertions(+)\n\ndiff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh\nindex 06c3c91762..cfd76bf987 100644\n--- a/t/lib-patch-mode.sh\n+++ b/t/lib-patch-mode.sh\n@@ -2,28 +2,40 @@\n \n . ./test-lib.sh\n \n+# set_state <path> <worktree-content> <index-content>\n+#\n+# Prepare the content for path in worktree and the index as specified.\n set_state () {\n \techo \"$3\" > \"$1\" &&\n \tgit add \"$1\" &&\n \techo \"$2\" > \"$1\"\n }\n \n+# save_state <path>\n+#\n+# Save index/worktree content of <path> in the files _worktree_<path>\n+# and _index_<path>\n save_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tcat \"$1\" > _worktree_\"$noslash\" &&\n \tgit show :\"$1\" > _index_\"$noslash\"\n }\n \n+# set_and_save_state <path> <worktree-content> <index-content>\n set_and_save_state () {\n \tset_state \"$@\" &&\n \tsave_state \"$1\"\n }\n \n+# verify_state <path> <expected-worktree-content> <expected-index-content>\n verify_state () {\n \ttest \"$(cat \"$1\")\" = \"$2\" &&\n \ttest \"$(git show :\"$1\")\" = \"$3\"\n }\n \n+# verify_saved_state <path>\n+#\n+# Call verify_state with expected contents from the last save_state\n verify_saved_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tverify_state \"$1\" \"$(cat _worktree_\"$noslash\")\" \"$(cat _index_\"$noslash\")\"\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nnew file mode 100755\nindex 0000000000..df91bf54bc\n--- /dev/null\n+++ b/t/t2070-restore.sh\n@@ -0,0 +1,77 @@\n+#!/bin/sh\n+\n+test_description='restore basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\techo first-and-a-half >>first.t &&\n+\tgit add first.t &&\n+\ttest_commit second &&\n+\techo one >one &&\n+\techo two >two &&\n+\techo untracked >untracked &&\n+\techo ignored >ignored &&\n+\techo /ignored >.gitignore &&\n+\tgit add one two .gitignore &&\n+\tgit update-ref refs/heads/one master\n+'\n+\n+test_expect_success 'restore without pathspec is not ok' '\n+\ttest_must_fail git restore &&\n+\ttest_must_fail git restore --source=first\n+'\n+\n+test_expect_success 'restore -p without pathspec is fine' '\n+\techo q >cmd &&\n+\tgit restore -p <cmd\n+'\n+\n+test_expect_success 'restore a file, ignoring branch of same name' '\n+\tcat one >expected &&\n+\techo dirty >>one &&\n+\tgit restore one &&\n+\ttest_cmp expected one\n+'\n+\n+test_expect_success 'restore a file on worktree from another branch' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first first.t &&\n+\ttest_cmp expected first.t &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'restore a file in the index from another branch' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --index first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore a file in both the index and worktree from another branch' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --index --worktree first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore --index uses HEAD as source' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob :./first.t >expected &&\n+\techo index-dirty >>first.t &&\n+\tgit add first.t &&\n+\tgit restore --index first.t &&\n+\tgit cat-file blob :./first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\ndiff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\nnew file mode 100755\nindex 0000000000..46ebcb2413\n--- /dev/null\n+++ b/t/t2071-restore-patch.sh\n@@ -0,0 +1,105 @@\n+#!/bin/sh\n+\n+test_description='git restore --patch'\n+\n+. ./lib-patch-mode.sh\n+\n+test_expect_success PERL 'setup' '\n+\tmkdir dir &&\n+\techo parent >dir/foo &&\n+\techo dummy >bar &&\n+\tgit add bar dir/foo &&\n+\tgit commit -m initial &&\n+\ttest_tick &&\n+\ttest_commit second dir/foo head &&\n+\tset_and_save_state bar bar_work bar_index &&\n+\tsave_head\n+'\n+\n+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'\n+\n+test_expect_success PERL 'saying \"n\" does nothing' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n n | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_saved_state dir/foo\n+'\n+\n+test_expect_success PERL 'git restore -p' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'git restore -p with staged changes' '\n+\tset_state dir/foo work index &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD^' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD^ &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent index\n+'\n+\n+test_expect_success PERL 'git restore -p handles deletion' '\n+\tset_state dir/foo work index &&\n+\trm dir/foo &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+# The idea in the rest is that bar sorts first, so we always say 'y'\n+# first and if the path limiter fails it'll apply to bar instead of\n+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in\n+# the failure case (and thus get out of the loop).\n+\n+test_expect_success PERL 'path limiting works: dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: -- dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | git restore -p --source=HEAD^ -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent head\n+'\n+\n+test_expect_success PERL 'path limiting works: foo inside dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | (cd dir && git restore -p foo) &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'none of this moved HEAD' '\n+\tverify_saved_head\n+'\n+\n+test_done\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370982","messageId":"20190308101655.9767-11-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 10/11] completion: support restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:54Z","receivedAt":"2019-03-08T10:17:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion for restore is straightforward. We could still do better\nthough by give the list of just tracked files instead of all present\nones. But let's leave it for later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 7fcf28d437..0b22e68187 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2483,6 +2483,21 @@ _git_reset ()\n \t__git_complete_refs\n }\n \n+_git_restore ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--source=*)\n+\t\t__git_complete_refs --cur=\"${cur##--source=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin restore\n+\t\t;;\n+\tesac\n+}\n+\n __git_revert_inprogress_options=\"--continue --quit --abort\"\n \n _git_revert ()\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370983","messageId":"20190308101655.9767-12-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T10:16:55Z","receivedAt":"2019-03-08T10:17:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git restore\" (together with \"git switch\") are added\nto avoid the confusion of one-command-do-all \"git checkout\" for new\nusers. They are also helpful to avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands.\n\nOne nice thing about git-restore is the ability to restore\n\"everything\", so it can be used in \"git status\" advice instead of both\n\"git checkout\" and \"git reset\".  The three commands suggested by \"git\nstatus\" are add, rm and restore.\n\n\"git checkout\" is also removed from \"git help\" (i.e. it's no longer\nconsidered a commonly used command)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clean.txt        |  2 +-\n Documentation/git-commit.txt       |  4 +-\n Documentation/git-format-patch.txt |  2 +-\n Documentation/git-reset.txt        |  6 +--\n Documentation/git-revert.txt       |  4 +-\n Documentation/gitcli.txt           |  4 +-\n Documentation/giteveryday.txt      |  4 +-\n Documentation/gittutorial-2.txt    |  4 +-\n Documentation/gittutorial.txt      |  2 +-\n Documentation/user-manual.txt      | 14 +++--\n builtin/clone.c                    |  2 +-\n builtin/commit.c                   |  2 +-\n command-list.txt                   |  2 +-\n t/t7508-status.sh                  | 82 +++++++++++++++---------------\n t/t7512-status-help.sh             | 20 ++++----\n wt-status.c                        | 26 +++++++---\n 16 files changed, 95 insertions(+), 85 deletions(-)\n\ndiff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt\nindex 03056dad0d..8a31ccffea 100644\n--- a/Documentation/git-clean.txt\n+++ b/Documentation/git-clean.txt\n@@ -64,7 +64,7 @@ OPTIONS\n \tdirectory) and $GIT_DIR/info/exclude, but do still use the ignore\n \trules given with `-e` options.  This allows removing all untracked\n \tfiles, including build products.  This can be used (possibly in\n-\tconjunction with 'git reset') to create a pristine\n+\tconjunction with 'git restore' or 'git reset') to create a pristine\n \tworking directory to test a clean build.\n \n -X::\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex a85c2c2a4c..e7ac8eb9e8 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -56,7 +56,7 @@ summary of what is included by any of the above for the next\n commit by giving the same set of parameters (options and paths).\n \n If you make a commit and then find a mistake immediately after\n-that, you can recover from it with 'git reset'.\n+that, you can recover from it with 'git restore' or 'git reset'.\n \n \n OPTIONS\n@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in\n your working tree are temporarily stored to a staging area\n called the \"index\" with 'git add'.  A file can be\n reverted back, only in the index but not in the working tree,\n-to that of the last commit with `git reset HEAD -- <file>`,\n+to that of the last commit with `git restore --index <file>`,\n which effectively reverts 'git add' and prevents the changes to\n this file from participating in the next commit.  After building\n the state to be committed incrementally with these commands,\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 0a24a5679e..71c9fe3af3 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:\n \n     $ git fetch <project> master:test-apply\n     $ git switch test-apply\n-    $ git reset --hard\n+    $ git restore --source=HEAD --index --worktree :/\n     $ git am a.patch\n \n If it does not apply correctly, there can be various reasons.\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex cbf901efb4..b70281677f 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -28,9 +28,9 @@ This means that `git reset <paths>` is the opposite of `git add\n <paths>`.\n +\n After running `git reset <paths>` to update the index entry, you can\n-use linkgit:git-checkout[1] to check the contents out of the index to\n-the working tree.\n-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you\n+use linkgit:git-restore[1] to check the contents out of the index to\n+the working tree. Alternatively, using linkgit:git-restore[1]\n+and specifying a commit with `--source`, you\n can copy the contents of a path out of a commit to the index and to the\n working tree in one go.\n \ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..c38bc54439 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to\n throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n-should see linkgit:git-checkout[1], specifically the `git checkout\n-<commit> -- <filename>` syntax.  Take care with these alternatives as\n+should see linkgit:git-restore[1], specifically the `--source`\n+option  Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n OPTIONS\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 592e06d839..e2a9674297 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n    things:\n +\n --------------------------------\n-$ git checkout -- *.c\n-$ git checkout -- \\*.c\n+$ git restore *.c\n+$ git restore \\*.c\n --------------------------------\n +\n The former lets your shell expand the fileglob, and you are asking\ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex ad455f3e39..79517b22f9 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -51,7 +51,7 @@ following commands.\n \n   * linkgit:git-commit[1] to advance the current branch.\n \n-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with\n+  * linkgit:git-reset[1] and linkgit:git-restore[1] (with\n     pathname parameters) to undo changes.\n \n   * linkgit:git-merge[1] to merge between local branches.\n@@ -82,7 +82,7 @@ Create a topic branch and develop.::\n ------------\n $ git switch -c alsa-audio <1>\n $ edit/compile/test\n-$ git checkout -- curses/ux_audio_oss.c <2>\n+$ git restore curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n $ edit/compile/test\n $ git diff HEAD <4>\ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e0976f6017..e412841488 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -370,13 +370,13 @@ situation:\n $ git status\n On branch master\n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   closing.txt\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   file.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex e6ad6b5f8d..6266ca21b4 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -110,7 +110,7 @@ $ git status\n On branch master\n Changes to be committed:\n Your branch is up to date with 'origin/master'.\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   file1\n \tmodified:   file2\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 94799faa2b..02713886f0 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess\n away, you can always return to the pre-merge state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git merge --abort\n -------------------------------------------------\n \n Or, if you've already committed the merge that you want to throw away,\n@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed\n state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git restore --index --worktree :/\n -------------------------------------------------\n \n If you make a commit that you later wish you hadn't, there are two\n@@ -1523,12 +1523,10 @@ Checking out an old version of a file\n \n In the process of undoing a previous bad change, you may find it\n useful to check out an older version of a particular file using\n-linkgit:git-checkout[1].  We've used `git checkout` before to switch\n-branches, but it has quite different behavior if it is given a path\n-name: the command\n+linkgit:git-restore[1]. The command\n \n -------------------------------------------------\n-$ git checkout HEAD^ path/to/file\n+$ git restore --source=HEAD^ path/to/file\n -------------------------------------------------\n \n replaces path/to/file by the contents it had in the commit HEAD^, and\n@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.\n The Workflow\n ------------\n \n-High-level operations such as linkgit:git-commit[1],\n-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data\n+High-level operations such as linkgit:git-commit[1] and\n+linkgit:git-restore[1] work by moving data\n between the working tree, the index, and the object database.  Git\n provides low-level operations which perform each of these steps\n individually.\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 50bde99618..024eb57996 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -492,7 +492,7 @@ static enum {\n static const char junk_leave_repo_msg[] =\n N_(\"Clone succeeded, but checkout failed.\\n\"\n    \"You can inspect what was checked out with 'git status'\\n\"\n-   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n+   \"and retry with 'git restore --source=HEAD :/'\\n\");\n \n static void remove_junk(void)\n {\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..c530264e63 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n-\t\t      \"not exceeded, and then \\\"git reset HEAD\\\" to recover.\"));\n+\t\t      \"not exceeded, and then \\\"git restore --index :/\\\" to recover.\"));\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))\n \t\twrite_commit_graph_reachable(get_object_directory(), 0, 0);\ndiff --git a/command-list.txt b/command-list.txt\nindex b9eae1c258..cf8dccb439 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n git-check-ignore                        purehelpers\n git-check-mailmap                       purehelpers\n-git-checkout                            mainporcelain           history\n+git-checkout                            mainporcelain\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n git-cherry                              plumbinginterrogators          complete\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex e1f11293e2..d5cf0185b7 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -94,13 +94,13 @@ test_expect_success 'status --column' '\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --index <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -128,13 +128,13 @@ cat >expect <<\\EOF\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --index <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   ../dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   modified\n \n@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \t<GREEN>new file:   dir2/added<RESET>\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \t<RED>modified:   dir1/modified<RESET>\n \n@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   dir1/modified\n \n@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD^1 <file>...\" to unstage)\n+  (use \"git restore --source=HEAD^1 --index <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n   (commit or discard the untracked or modified content in submodules)\n \n \tmodified:   dir1/modified\n@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \tmodified:   sm (new commits)\n@@ -1379,13 +1379,13 @@ cat > expect << EOF\n ;   (use \"git pull\" to merge the remote branch into yours)\n ;\n ; Changes to be committed:\n-;   (use \"git reset HEAD <file>...\" to unstage)\n+;   (use \"git restore --index <file>...\" to unstage)\n ;\n ;\tmodified:   sm\n ;\n ; Changes not staged for commit:\n ;   (use \"git add <file>...\" to update what will be committed)\n-;   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+;   (use \"git restore <file>...\" to discard changes in working directory)\n ;\n ;\tmodified:   dir1/modified\n ;\tmodified:   sm (new commits)\n@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 458608cc1e..6e678456bc 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -85,7 +85,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -110,7 +110,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -148,7 +148,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -176,7 +176,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\\''split_commit'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   to-revert.txt\n@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --index <file>...\" to unstage)\n \n \tmodified:   to-revert.txt\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..3098e77d72 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t;\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --index <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --index <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \n \tif (!both_deleted) {\n@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t; /* NEEDSWORK: use \"git reset --unresolve\"??? */\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c\n+\t\t\t\t\t , _(\"  (use \\\"git restore --index <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --index <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n \telse\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n-\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n+\tstatus_printf_ln(s, c, _(\"  (use \\\"git restore <file>...\\\" to discard changes in working directory)\"));\n \tif (has_dirty_submodules)\n \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"371004","messageId":"CABPp-BFXZMorrHph3hGFnqfceHs68byWNgffNKGp1ov6X5-o5A@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-2-pclouds@gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-08T18:01:25Z","receivedAt":"2019-03-08T18:01:40Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Thanks for working on this; overall looks really good.  I've got a few\ncomments here and there on the wording and combinations of options...\n\nOn Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> +SYNOPSIS\n\nIt might be worth adding some words somewhere to differentiate between\n`reset` and `restore`.  E.g.\n\n`git restore` modifies the working tree (and maybe index) to change\nfile content to match some other (usually older) version, but does not\nupdate your branch.  `git reset` is about modifying which commit your\nbranch points to, meaning possibly removing and/or adding many commits\nto your branch.\n\nIt may also make sense to add whatever description you use to the reset manpage.\n\n> +--------\n> +[verse]\n> +'git restore' [<options>] [--source=<revision>] <pathspec>...\n> +'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]\n\nSo one cannot specify any special options with -p?  Does that mean one\ncannot use it with --index (i.e. this command cannot replace 'git\nreset -p')?  Or is this an oversight in the synopsis?\n\n> +DESCRIPTION\n> +-----------\n> +Restore paths in the working tree by replacing with the contents in\n> +the restore source or remove if the paths do not exist in the restore\n> +source. The source is by default the index but could be from a commit.\n> +The command can also optionally restore content in the index from a\n> +commit.\n\nThe first sentence makes it sound like two different operations, when\nI think it is one. Perhaps:\n\nRestore paths in the working tree by replacing with the contents in\nthe restore source (and if the restore source is missing paths found\nin the working tree, that means deleting those paths from the working\ntree).\n\n> +\n> +When a `<revision>` is given, the paths that match the `<pathspec>` are\n> +updated both in the index and in the working tree.\n\nI thought the default was --worktree.  Is this sentence from an older\nversion of your patch series that you forgot to update?\n\n> +\n> +OPTIONS\n> +-------\n> +-s<tree>::\n> +--source=<tree>::\n> +       Restore the working tree files with the content from the given\n> +       tree or any revision that leads to a tree (e.g. a commit or a\n> +       branch).\n\nI think that's a little hard to parse.  We may not be able to avoid\n\"working tree\" vs. \"tree\" confusion, but the spelling of <tree> feels\nlike it should be a separate sentence.  Maybe:\n\nRestore the working tree files with the content from the given tree.\nIt is common to specify the source tree by naming a commit, branch, or\ntag.\n\n?\n\n> +\n> +-p::\n> +--patch::\n> +       Interactively select hunks in the difference between the\n> +       `<revision>` (or the index, if unspecified) and the working\n> +       tree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n> +       to learn how to operate the `--patch` mode.\n> +\n> +-W::\n> +--worktree::\n> +-I::\n> +--index::\n> +       Specify the restore location. If neither option is specified,\n> +       by default the working tree is restored. If `--index` is\n> +       specified without `--worktree` or `--source`, `--source=HEAD`\n> +       is implied. These options only make sense to use with\n> +       `--source`.\n\nSeems like this contains a minor contradiction.  Perhaps start the\nfinal sentence with: \"Otherwise, ...\" ?\n\n> +-q::\n> +--quiet::\n> +       Quiet, suppress feedback messages.\n> +\n> +--progress::\n> +--no-progress::\n> +       Progress status is reported on the standard error stream\n> +       by default when it is attached to a terminal, unless `--quiet`\n> +       is specified. This flag enables progress reporting even if not\n> +       attached to a terminal, regardless of `--quiet`.\n\nI'm assuming this means there are feedback messages other than\nprogress feedback?\n\n> +-f::\n> +--force::\n> +       If `--source` is not specified, unmerged entries are left alone\n> +       and will not fail the operation. Unmerged entries are always\n> +       replaced if `--source` is specified, regardless of `--force`.\n\nThis may be slightly confusing, in particular it suggests that --index\n(or --worktree and --index) are the default.  Is --force only useful\nwhen --index is specified?  If it has utility with --worktree only,\nwhat does it do?  Also, what happens when there are unmerged entries\nin the index and someone tries to restore just working tree files --\nare the ones corresponding to unmerged entries skipped (if so,\nsilently or with warnings printed for the user?), or does something\nelse happen?\n\n> +--ours::\n> +--theirs::\n> +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged\n> +       paths.\n> ++\n> +Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> +'theirs' may appear swapped. See the explanation of the same options\n> +in linkgit:git-checkout[1] for details.\n\nSo sad, but yes you need to mention it.  I'm curious what we say for\ncherry-pick; it's not clear to me whether people think of the current\nbranch as \"ours\" or the commit they wrote themselves and are trying to\nbring to this branch as \"ours\".  There are probably examples of both.\n\nNot that I think you can do anything about that here or need to change\nyour description.  I'm just very sad about --ours and --theirs in\ngeneral.\n\n> +\n> +-m::\n> +--merge::\n> +       Recreate the conflicted merge in the specified paths.\n> +\n> +--conflict=<style>::\n> +       The same as `--merge` option above, but changes the way the\n> +       conflicting hunks are presented, overriding the merge.conflictStyle\n> +       configuration variable.  Possible values are \"merge\" (default)\n> +       and \"diff3\" (in addition to what is shown by \"merge\" style,\n> +       shows the original contents).\n\nShould you mention that these are incompatible with --source and\n--index?  And perhaps also make sure the code aborts if either of\nthese options are combined with either of those?\n\n> +--ignore-skip-worktree-bits::\n> +       In sparse checkout mode, by default update only entries\n> +       matched by `<pathspec>` and sparse patterns in\n> +       $GIT_DIR/info/sparse-checkout. This option ignores the sparse\n> +       patterns and unconditionally restores any files in `<pathspec>`.\n\nThe first sentence is slightly confusing; it sounds like you are\nsaying what the flag does rather than what the default is without the\nflag.  Perhaps:\n\n\"In sparse checkout mode, the default is to only update entries\nmatched by `<pathspec>` and sparse patterns in\n$GIT_DIR/info/sparse-checkout....\"\n\n> +\n> +--recurse-submodules::\n> +--no-recurse-submodules::\n> +       Using `--recurse-submodules` will update the content of all initialized\n> +       submodules according to the commit recorded in the superproject. If\n> +       local modifications in a submodule would be overwritten the checkout\n> +       will fail unless `-f` is used.\n\nThis suggests that your documentation for -f/--force is incomplete.\n\n> +       If nothing (or `--no-recurse-submodules`)\n> +       is used, the work trees of submodules will not be updated.\n\nThis seems slightly awkward.  Perhaps \"If `--no-recurse-submodules`\n(which is the default) is used, the work trees of submodules will not\nbe updated.\n\n> +       Just like linkgit:git-submodule[1], this will detach the\n> +       submodules HEAD.\n> +\n> +--overlay::\n> +--no-overlay::\n> +       In overlay mode, `git checkout` never removes files from the\n\nWhy are you talking about `git checkout` here?  Shouldn't this be `git restore`?\n\n> +       index or the working tree. In no-overlay mode, files that\n> +       appear in the index and working tree, but not in `--source` tree\n> +       are removed, to make them match `<tree-ish>` exactly. The\n> +       default is no-overlay mode.\n\nA minor comment: This seems slightly weird because it feels like you\nstart out with a negative (\"never removes\") and then describe the\nopposite option which adds another negation to the negative.  But the\nwording avoids an explicit double negative (which is good), and I\ndon't have a better suggestion for wording here.  Just thought I'd\nflag it in case anyone else reading over this notices it too and has a\nwording suggestion.\n\n> +\n> +EXAMPLES\n> +--------\n> +\n> +The following sequence checks out the `master` branch, reverts\n> +the `Makefile` to two revisions back, deletes hello.c by\n> +mistake, and gets it back from the index.\n> +\n> +------------\n> +$ git switch master\n> +$ git restore --source master~2 Makefile  <1>\n> +$ rm -f hello.c\n> +$ git restore hello.c                   <2>\n> +------------\n> +\n> +<1> take a file out of another commit\n> +<2> restore hello.c from the index\n> +\n> +If you want to check out _all_ C source files out of the index,\n> +you can say\n> +\n> +------------\n> +$ git restore '*.c'\n> +------------\n> +\n> +Note the quotes around `*.c`.  The file `hello.c` will also be\n> +checked out, even though it is no longer in the working tree,\n> +because the file globbing is used to match entries in the index\n> +(not in the working tree by the shell).\n> +\n> +To restore all files in the current directory\n> +\n> +------------\n> +$ git restore .\n> +------------\n> +\n> +or to restore all working tree files with 'top' pathspec magic (see\n> +linkgit::gitglossary[7])\n> +\n> +------------\n> +$ git restore :/\n> +------------\n> +\n> +To restore a file in the index only (this is the same as using\n> +\"git reset\")\n> +\n> +------------\n> +$ git restore --index hello.c\n> +------------\n> +\n> +or you can restore both the index and the working tree\n> +\n> +------------\n> +$ git restore --source=HEAD --index --worktree hello.c\n> +------------\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-checkout[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> diff --git a/Makefile b/Makefile\n> index 8e91db73ad..ffe7e4f58f 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X\n>  BUILT_INS += git-fsck-objects$X\n>  BUILT_INS += git-init$X\n>  BUILT_INS += git-merge-subtree$X\n> +BUILT_INS += git-restore$X\n>  BUILT_INS += git-show$X\n>  BUILT_INS += git-stage$X\n>  BUILT_INS += git-status$X\n> diff --git a/builtin.h b/builtin.h\n> index c64e44450e..6830000e14 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n>  extern int cmd_repack(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n>  extern int cmd_reset(int argc, const char **argv, const char *prefix);\n> +extern int cmd_restore(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n>  extern int cmd_revert(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 4903359b49..11dd2ae44c 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -37,6 +37,11 @@ static const char * const switch_branch_usage[] = {\n>         NULL,\n>  };\n>\n> +static const char * const restore_files_usage[] = {\n> +       N_(\"git restore [<options>] [<branch>] -- <file>...\"),\n> +       NULL,\n> +};\n> +\n>  struct checkout_opts {\n>         int patch_mode;\n>         int quiet;\n> @@ -1528,3 +1533,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>         FREE_AND_NULL(options);\n>         return ret;\n>  }\n> +\n> +int cmd_restore(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct checkout_opts opts;\n> +       struct option *options = NULL;\n> +       int ret;\n> +\n> +       memset(&opts, 0, sizeof(opts));\n> +       opts.dwim_new_local_branch = 1;\n> +       opts.switch_branch_doing_nothing_is_ok = 0;\n> +       opts.accept_pathspec = 1;\n> +\n> +       options = parse_options_dup(options);\n> +       options = add_common_options(&opts, options);\n> +       options = add_checkout_path_options(&opts, options);\n> +\n> +       ret = checkout_main(argc, argv, prefix, &opts,\n> +                           options, restore_files_usage);\n> +       FREE_AND_NULL(options);\n> +       return ret;\n> +}\n> diff --git a/command-list.txt b/command-list.txt\n> index 13317f47d4..b9eae1c258 100644\n> --- a/command-list.txt\n> +++ b/command-list.txt\n> @@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n>  git-request-pull                        foreignscminterface             complete\n>  git-rerere                              ancillaryinterrogators\n>  git-reset                               mainporcelain           worktree\n> +git-restore                             mainporcelain           worktree\n>  git-revert                              mainporcelain\n>  git-rev-list                            plumbinginterrogators\n>  git-rev-parse                           plumbinginterrogators\n> diff --git a/git.c b/git.c\n> index 39582cf511..6d439e723f 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n>         { \"replace\", cmd_replace, RUN_SETUP },\n>         { \"rerere\", cmd_rerere, RUN_SETUP },\n>         { \"reset\", cmd_reset, RUN_SETUP },\n> +       { \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n>         { \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n>         { \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n>         { \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n> --\n> 2.21.0.rc1.337.gdf7f8d0522\n>\n"},{"id":"371024","messageId":"CACsJy8D4tvm_zLo0DcnjmcBeKDRDR+HGAxd1PsUMSBcgR59DPg@mail.gmail.com","threadId":"50690","inReplyTo":"CABPp-BFXZMorrHph3hGFnqfceHs68byWNgffNKGp1ov6X5-o5A@mail.gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-09T12:16:00Z","receivedAt":"2019-03-09T12:16:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> Thanks for working on this; overall looks really good.  I've got a few\n> comments here and there on the wording and combinations of options...\n>\n> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > +SYNOPSIS\n>\n> It might be worth adding some words somewhere to differentiate between\n> `reset` and `restore`.  E.g.\n>\n> `git restore` modifies the working tree (and maybe index) to change\n> file content to match some other (usually older) version, but does not\n> update your branch.  `git reset` is about modifying which commit your\n> branch points to, meaning possibly removing and/or adding many commits\n> to your branch.\n>\n> It may also make sense to add whatever description you use to the reset manpage.\n\nGood point.\n\n> > +--------\n> > +[verse]\n> > +'git restore' [<options>] [--source=<revision>] <pathspec>...\n> > +'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]\n>\n> So one cannot specify any special options with -p?  Does that mean one\n> cannot use it with --index (i.e. this command cannot replace 'git\n> reset -p')?  Or is this an oversight in the synopsis?\n\nOversight. -p can be used with either --index or --worktree or both.\n\n> > +\n> > +When a `<revision>` is given, the paths that match the `<pathspec>` are\n> > +updated both in the index and in the working tree.\n>\n> I thought the default was --worktree.  Is this sentence from an older\n> version of your patch series that you forgot to update?\n\nOops.\n\n> > +-q::\n> > +--quiet::\n> > +       Quiet, suppress feedback messages.\n> > +\n> > +--progress::\n> > +--no-progress::\n> > +       Progress status is reported on the standard error stream\n> > +       by default when it is attached to a terminal, unless `--quiet`\n> > +       is specified. This flag enables progress reporting even if not\n> > +       attached to a terminal, regardless of `--quiet`.\n>\n> I'm assuming this means there are feedback messages other than\n> progress feedback?\n\nThere could be. This is carried over from git-checkout. I suspect this\nis about warnings that we print from time to time.\n\n> > +-f::\n> > +--force::\n> > +       If `--source` is not specified, unmerged entries are left alone\n> > +       and will not fail the operation. Unmerged entries are always\n> > +       replaced if `--source` is specified, regardless of `--force`.\n>\n> This may be slightly confusing, in particular it suggests that --index\n> (or --worktree and --index) are the default.  Is --force only useful\n> when --index is specified?  If it has utility with --worktree only,\n> what does it do?\n\nWell, this is 'git checkout -f' behavior which only concerns the\nindex. So yeah it only matters with --index.\n\n> Also, what happens when there are unmerged entries\n> in the index and someone tries to restore just working tree files --\n> are the ones corresponding to unmerged entries skipped (if so,\n> silently or with warnings printed for the user?), or does something\n> else happen?\n\nIf -m is also specified, then we recreate the conflict. The from code,\nif an unmerged path is skipped, there will be warnings.\n\n> > +\n> > +-m::\n> > +--merge::\n> > +       Recreate the conflicted merge in the specified paths.\n> > +\n> > +--conflict=<style>::\n> > +       The same as `--merge` option above, but changes the way the\n> > +       conflicting hunks are presented, overriding the merge.conflictStyle\n> > +       configuration variable.  Possible values are \"merge\" (default)\n> > +       and \"diff3\" (in addition to what is shown by \"merge\" style,\n> > +       shows the original contents).\n>\n> Should you mention that these are incompatible with --source and\n> --index?  And perhaps also make sure the code aborts if either of\n> these options are combined with either of those?\n\nI will make sure that the code aborts. Not so sure about mentionging\nevery incompatible combination though. Will it be too much? I think we\ncatch and report plenty invalid combinations but I don't think we have\nmentioned them all (or maybe we have, I didn't check the document\nagain)\n\n> > +       Just like linkgit:git-submodule[1], this will detach the\n> > +       submodules HEAD.\n> > +\n> > +--overlay::\n> > +--no-overlay::\n> > +       In overlay mode, `git checkout` never removes files from the\n>\n> Why are you talking about `git checkout` here?  Shouldn't this be `git restore`?\n\nOops.\n-- \nDuy\n"},{"id":"371035","messageId":"053a95ba-3063-b1bd-579c-a0c37f24e51a@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-10-pclouds@gmail.com","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Andrei Rybak","fromEmail":"rybak.a.v@gmail.com","sentAt":"2019-03-09T16:53:45Z","receivedAt":"2019-03-09T16:53:50Z","isPatch":true,"sender":{"key":"rybak.a.v@gmail.com","avatar":"https://avatars.githubusercontent.com/u/624072?v=4"},"body":"On 3/8/19 11:16 AM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/lib-patch-mode.sh               |  12 ++++\n>  t/t2070-restore.sh (new +x)       |  77 ++++++++++++++++++++++\n>  t/t2071-restore-patch.sh (new +x) | 105 ++++++++++++++++++++++++++++++\n>  3 files changed, 194 insertions(+)\n> \n> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\n> new file mode 100755\n> index 0000000000..df91bf54bc\n> --- /dev/null\n> +++ b/t/t2070-restore.sh\n> @@ -0,0 +1,77 @@\n> +#!/bin/sh\n> +\n> +test_description='restore basic functionality'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\ttest_commit first &&\n> +\techo first-and-a-half >>first.t &&\n> +\tgit add first.t &&\n> +\ttest_commit second &&\n> +\techo one >one &&\n> +\techo two >two &&\n> +\techo untracked >untracked &&\n> +\techo ignored >ignored &&\n> +\techo /ignored >.gitignore &&\n> +\tgit add one two .gitignore &&\n> +\tgit update-ref refs/heads/one master\n> +'\n> +\n\n[snip]\n\n> +\n> +test_expect_success 'restore a file, ignoring branch of same name' '\n> +\tcat one >expected &&\n> +\techo dirty >>one &&\n> +\tgit restore one &&\n> +\ttest_cmp expected one\n> +'\n> +\n\nBranch 'one' has been created by update-ref invocation in the setup, OK.\n\n> +test_expect_success 'restore a file on worktree from another branch' '\n> +\ttest_when_finished git reset --hard &&\n> +\tgit cat-file blob first:./first.t >expected &&\n> +\tgit restore --source=first first.t &&\n> +\ttest_cmp expected first.t &&\n> +\tgit cat-file blob HEAD:./first.t >expected &&\n> +\tgit show :first.t >actual &&\n> +\ttest_cmp expected actual\n> +'\n\nTest description reads \"from another branch\". However \"first\", created by\ntest_commit invocation is a tag. Maybe description should read \"from another\nref\"? Same applies to other tests which utilize \"first\".\n\n> +\n> +test_expect_success 'restore a file in the index from another branch' '\n> +\ttest_when_finished git reset --hard &&\n> +\tgit cat-file blob first:./first.t >expected &&\n> +\tgit restore --source=first --index first.t &&\n> +\tgit show :first.t >actual &&\n> +\ttest_cmp expected actual &&\n> +\tgit cat-file blob HEAD:./first.t >expected &&\n> +\ttest_cmp expected first.t\n> +'\n> +\n> +test_expect_success 'restore a file in both the index and worktree from another branch' '\n> +\ttest_when_finished git reset --hard &&\n> +\tgit cat-file blob first:./first.t >expected &&\n> +\tgit restore --source=first --index --worktree first.t &&\n> +\tgit show :first.t >actual &&\n> +\ttest_cmp expected actual &&\n> +\ttest_cmp expected first.t\n> +'\n> +\n> +test_expect_success 'restore --index uses HEAD as source' '\n> +\ttest_when_finished git reset --hard &&\n> +\tgit cat-file blob :./first.t >expected &&\n> +\techo index-dirty >>first.t &&\n> +\tgit add first.t &&\n> +\tgit restore --index first.t &&\n> +\tgit cat-file blob :./first.t >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_done\n\n\n--\nBest regards, Andrei R.\n"},{"id":"371039","messageId":"CABPp-BHwp_36Pc-xbCC0_t=uO3td1fn0KYjQ9N2esznJpRt3ig@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-3-pclouds@gmail.com","subject":"Re: [PATCH v1 02/11] restore: take tree-ish from --source option instead","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:13:38Z","receivedAt":"2019-03-09T18:13:52Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> This is another departure from 'git checkout' syntax, which uses -- to\n> separate ref and pathspec. The observation is restore (or \"git\n> checkout ,, <pathspec>\") is most often used to restore some files from\n> the index. If this is correct, we can simplify it by taking a way the\n> ref, so that we can write\n>\n>     git restore some-file\n>\n> without worrying about some-file being a ref and whether we need to do\n>\n>     git restore -- some-file\n>\n> for safety. If the source of the restore comes from a tree, it will be\n> in the form of an option with value, e.g.\n>\n>     git restore --source=this-tree some-file\n>\n> This is of course longer to type than using \"--\". But hopefully it\n> will not be used as often, and it is clearly easier to understand.\n>\n> dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\n> because it's irrelevant because we don't really care about dwim-ing.\n> With accept_ref being unset, dwim can't happen.\n\nNice.  :-)\n\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------\n>  1 file changed, 34 insertions(+), 8 deletions(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 11dd2ae44c..838343d6aa 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {\n>  };\n>\n>  static const char * const restore_files_usage[] = {\n\nrestore_files_usage or restore_usage?\n\n> -       N_(\"git restore [<options>] [<branch>] -- <file>...\"),\n> +       N_(\"git restore [<options>] [--source=<branch>] <file>...\"),\n>         NULL,\n>  };\n>\n> @@ -57,6 +57,7 @@ struct checkout_opts {\n>         int count_checkout_paths;\n>         int overlay_mode;\n>         int dwim_new_local_branch;\n> +       int accept_ref;\n>         int accept_pathspec;\n>         int switch_branch_doing_nothing_is_ok;\n>         int only_merge_on_switching_branches;\n> @@ -72,6 +73,7 @@ struct checkout_opts {\n>         int branch_exists;\n>         const char *prefix;\n>         struct pathspec pathspec;\n> +       const char *from_treeish;\n>         struct tree *source_tree;\n>  };\n>\n> @@ -1324,6 +1326,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>  {\n>         struct branch_info new_branch_info;\n>         int dwim_remotes_matched = 0;\n> +       int parseopt_flags = 0;\n>\n>         memset(&new_branch_info, 0, sizeof(new_branch_info));\n>         opts->overwrite_ignore = 1;\n> @@ -1335,8 +1338,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>\n>         opts->track = BRANCH_TRACK_UNSPECIFIED;\n>\n> -       argc = parse_options(argc, argv, prefix, options, usagestr,\n> -                            PARSE_OPT_KEEP_DASHDASH);\n> +       if (!opts->accept_pathspec && !opts->accept_ref)\n> +               BUG(\"make up your mind, you need to take _something_\");\n\nhehe.  I secretly hope that someone eventually hits that, preferably\nanother git developer making some changes and not an end user, though.\n\n\n> +       if (opts->accept_pathspec && opts->accept_ref)\n> +               parseopt_flags = PARSE_OPT_KEEP_DASHDASH;\n> +\n> +       argc = parse_options(argc, argv, prefix, options,\n> +                            usagestr, parseopt_flags);\n>\n>         if (opts->show_progress < 0) {\n>                 if (opts->quiet)\n> @@ -1393,7 +1401,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>          * including \"last branch\" syntax and DWIM-ery for names of\n>          * remote branches, erroring out for invalid or ambiguous cases.\n>          */\n> -       if (argc) {\n> +       if (argc && opts->accept_ref) {\n>                 struct object_id rev;\n>                 int dwim_ok =\n>                         !opts->patch_mode &&\n> @@ -1405,6 +1413,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>                                              &dwim_remotes_matched);\n>                 argv += n;\n>                 argc -= n;\n> +       } else if (!opts->accept_ref && opts->from_treeish) {\n> +               struct object_id rev;\n> +\n> +               if (get_oid_mb(opts->from_treeish, &rev))\n\nGoing on a slight tangent from your series: get_oid_mb really deserves\na comment somewhere that \"mb\" is \"merge base\".  I had to spelunk\nthrough history to find it...\n\n> +                       die(_(\"could not resolve %s\"), opts->from_treeish);\n> +\n> +               setup_new_branch_info_and_source_tree(&new_branch_info,\n> +                                                     opts, &rev,\n> +                                                     opts->from_treeish);\n> +\n> +               if (!opts->source_tree)\n> +                       die(_(\"reference is not a tree: %s\"), opts->from_treeish);\n>         }\n>\n>         if (argc) {\n> @@ -1488,6 +1508,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>         opts.dwim_new_local_branch = 1;\n>         opts.switch_branch_doing_nothing_is_ok = 1;\n>         opts.only_merge_on_switching_branches = 0;\n> +       opts.accept_ref = 1;\n>         opts.accept_pathspec = 1;\n>         opts.implicit_detach = 1;\n>\n> @@ -1519,6 +1540,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>\n>         memset(&opts, 0, sizeof(opts));\n>         opts.dwim_new_local_branch = 0;\n> +       opts.accept_ref = 1;\n>         opts.accept_pathspec = 0;\n>         opts.switch_branch_doing_nothing_is_ok = 0;\n>         opts.only_merge_on_switching_branches = 1;\n> @@ -1537,15 +1559,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>  int cmd_restore(int argc, const char **argv, const char *prefix)\n>  {\n>         struct checkout_opts opts;\n> -       struct option *options = NULL;\n> +       struct option *options;\n> +       struct option restore_options[] = {\n> +               OPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n> +                          N_(\"where the checkout from\")),\n> +               OPT_END()\n> +       };\n>         int ret;\n>\n>         memset(&opts, 0, sizeof(opts));\n> -       opts.dwim_new_local_branch = 1;\n> -       opts.switch_branch_doing_nothing_is_ok = 0;\n> +       opts.accept_ref = 0;\n>         opts.accept_pathspec = 1;\n>\n> -       options = parse_options_dup(options);\n> +       options = parse_options_dup(restore_options);\n>         options = add_common_options(&opts, options);\n>         options = add_checkout_path_options(&opts, options);\n>\n> --\n> 2.21.0.rc1.337.gdf7f8d0522\n>\n"},{"id":"371041","messageId":"CABPp-BENtDUOpKYHZu=4rKFrQMaCE=fEtAWRqHvePjZYm1jTtg@mail.gmail.com","threadId":"50690","inReplyTo":"CACsJy8D4tvm_zLo0DcnjmcBeKDRDR+HGAxd1PsUMSBcgR59DPg@mail.gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:27:05Z","receivedAt":"2019-03-09T18:27:19Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 9, 2019 at 4:16 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:\n\n> > > +-q::\n> > > +--quiet::\n> > > +       Quiet, suppress feedback messages.\n> > > +\n> > > +--progress::\n> > > +--no-progress::\n> > > +       Progress status is reported on the standard error stream\n> > > +       by default when it is attached to a terminal, unless `--quiet`\n> > > +       is specified. This flag enables progress reporting even if not\n> > > +       attached to a terminal, regardless of `--quiet`.\n> >\n> > I'm assuming this means there are feedback messages other than\n> > progress feedback?\n>\n> There could be. This is carried over from git-checkout. I suspect this\n> is about warnings that we print from time to time.\n\nWhy would --quiet squelch warnings?  I figured it'd only squelch\nfeedback, informational, or progress messages.\n\nI understand you just carried it over from git-checkout, but as worded\nit makes me wonder if checkout has suboptimal behavior or perhaps just\na suboptimal explanation of its flags ... and if it does, we probably\ndon't want to carry that over.\n\n> > > +-f::\n> > > +--force::\n> > > +       If `--source` is not specified, unmerged entries are left alone\n> > > +       and will not fail the operation. Unmerged entries are always\n> > > +       replaced if `--source` is specified, regardless of `--force`.\n> >\n> > This may be slightly confusing, in particular it suggests that --index\n> > (or --worktree and --index) are the default.  Is --force only useful\n> > when --index is specified?  If it has utility with --worktree only,\n> > what does it do?\n>\n> Well, this is 'git checkout -f' behavior which only concerns the\n> index. So yeah it only matters with --index.\n\nOkay, good to know that this only matters with --index. However, new\nproblem: This makes the explanation feel contradictory, though,\nbecause elsewhere you stated that --source=HEAD is implied when\n--index is given without a source.  So, the combination of this\ndescription and that fact suggests that -f is either useless (--index\nis not specified) or ignored (because --source will either default to\nHEAD or be specified by the user).  Maybe that's true and -f should be\nremoved from this new document.  If it has actual utility in some\ncases, then this description needs to be reworked to explain what\nthose circiumstances are somehow.\n\n> > Also, what happens when there are unmerged entries\n> > in the index and someone tries to restore just working tree files --\n> > are the ones corresponding to unmerged entries skipped (if so,\n> > silently or with warnings printed for the user?), or does something\n> > else happen?\n>\n> If -m is also specified, then we recreate the conflict. The from code,\n> if an unmerged path is skipped, there will be warnings.\n\nNot sure I understand.  Are you saying that if -m is not specified and\nnor is --source or --index, that we print a warning for each unmerged\nentry specified by the pathspecs?\n\n> > > +\n> > > +-m::\n> > > +--merge::\n> > > +       Recreate the conflicted merge in the specified paths.\n> > > +\n> > > +--conflict=<style>::\n> > > +       The same as `--merge` option above, but changes the way the\n> > > +       conflicting hunks are presented, overriding the merge.conflictStyle\n> > > +       configuration variable.  Possible values are \"merge\" (default)\n> > > +       and \"diff3\" (in addition to what is shown by \"merge\" style,\n> > > +       shows the original contents).\n> >\n> > Should you mention that these are incompatible with --source and\n> > --index?  And perhaps also make sure the code aborts if either of\n> > these options are combined with either of those?\n>\n> I will make sure that the code aborts. Not so sure about mentioning\n> every incompatible combination though. Will it be too much? I think we\n> catch and report plenty invalid combinations but I don't think we have\n> mentioned them all (or maybe we have, I didn't check the document\n> again)\n\nMaking sure the code aborts is probably good enough.\n\n\nElijah\n"},{"id":"371043","messageId":"CABPp-BFo2XMg4VMBE=5XmtJq+bmAyBFD6e5ofwvjQndJy=Vr3A@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-4-pclouds@gmail.com","subject":"Re: [PATCH v1 03/11] restore: make pathspec mandatory","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:35:41Z","receivedAt":"2019-03-09T18:35:56Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> \"git restore\" without arguments does not make much sense when\n> it's about restoring files (what files now?). We could default to\n> either\n>\n>     git restore .\n>\n> or\n>\n>     git restore :/\n>\n> Neither is intuitive. Make the user always give pathspec, force the\n> user to think the scope of restore they want because this is a\n> destructive operation.\n>\n> \"git restore -p\" without pathspec is an exception to this\n> because it really is a separate mode. It will be treated as running\n> patch mode on the whole worktree.\n\nThis all sounds very good.\n\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 7 +++++++\n>  1 file changed, 7 insertions(+)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 838343d6aa..c52ce13d2a 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -61,6 +61,7 @@ struct checkout_opts {\n>         int accept_pathspec;\n>         int switch_branch_doing_nothing_is_ok;\n>         int only_merge_on_switching_branches;\n> +       int empty_pathspec_ok;\n>\n>         const char *new_branch;\n>         const char *new_branch_force;\n> @@ -1427,6 +1428,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>                         die(_(\"reference is not a tree: %s\"), opts->from_treeish);\n>         }\n>\n> +       if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&\n> +           !opts->patch_mode)  /* patch mode is special */\n> +               die(_(\"pathspec is required\"));\n\nMaybe\n   die(_(\"you must specify path(s) to restore\"));\n?  While \"pathspec\" is something we use in a few places, I don't think\nusers intuitively know what it is.  Also, I just looked up the manpage\nagain, and it looks like you use \"pathspec\" in the restore manpage,\nbut don't define it.\n\n> +\n>         if (argc) {\n>                 parse_pathspec(&opts->pathspec, 0,\n>                                opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n> @@ -1511,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>         opts.accept_ref = 1;\n>         opts.accept_pathspec = 1;\n>         opts.implicit_detach = 1;\n> +       opts.empty_pathspec_ok = 1;\n>\n>         options = parse_options_dup(checkout_options);\n>         options = add_common_options(&opts, options);\n> @@ -1570,6 +1576,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n>         memset(&opts, 0, sizeof(opts));\n>         opts.accept_ref = 0;\n>         opts.accept_pathspec = 1;\n> +       opts.empty_pathspec_ok = 0;\n>\n>         options = parse_options_dup(restore_options);\n>         options = add_common_options(&opts, options);\n> --\n> 2.21.0.rc1.337.gdf7f8d0522\n"},{"id":"371044","messageId":"CABPp-BGG-QcUp+RWVTVBXfQqRJ1n8sc_nFNK1Pr-y=x-aAqODw@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-5-pclouds@gmail.com","subject":"Re: [PATCH v1 04/11] restore: disable overlay mode by default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:37:55Z","receivedAt":"2019-03-09T18:38:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Overlay mode is considered confusing when the command is about\n> restoring files on worktree. Disable it by default. The user can still\n> turn it on, or use 'git checkout' which still has overlay mode on by\n> default.\n>\n> While at there make the check in checkout_branch() stricter. Neither\n> --overlay or --no-overlay should be accepted in branch switching mode.\n\ns/While at there/While at it,/ ?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 11 +++++++----\n>  1 file changed, 7 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index c52ce13d2a..9e59bf792f 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -1196,9 +1196,9 @@ static int checkout_branch(struct checkout_opts *opts,\n>                 die(_(\"'%s' cannot be used with switching branches\"),\n>                     \"--patch\");\n>\n> -       if (!opts->overlay_mode)\n> +       if (opts->overlay_mode != -1)\n>                 die(_(\"'%s' cannot be used with switching branches\"),\n> -                   \"--no-overlay\");\n> +                   \"--[no]-overlay\");\n>\n>         if (opts->writeout_stage)\n>                 die(_(\"'%s' cannot be used with switching branches\"),\n> @@ -1313,7 +1313,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n>                 OPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n>                 OPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n>                          N_(\"do not limit pathspecs to sparse entries only\")),\n> -               OPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n>                 OPT_END()\n>         };\n>         struct option *newopts = parse_options_concat(prevopts, options);\n> @@ -1333,7 +1332,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>         opts->overwrite_ignore = 1;\n>         opts->prefix = prefix;\n>         opts->show_progress = -1;\n> -       opts->overlay_mode = -1;\n>\n>         git_config(git_checkout_config, opts);\n>\n> @@ -1505,6 +1503,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>                 OPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n>                 OPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n>                          N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n> +               OPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n>                 OPT_END()\n>         };\n>         int ret;\n> @@ -1517,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>         opts.accept_pathspec = 1;\n>         opts.implicit_detach = 1;\n>         opts.empty_pathspec_ok = 1;\n> +       opts.overlay_mode = -1;\n>\n>         options = parse_options_dup(checkout_options);\n>         options = add_common_options(&opts, options);\n> @@ -1551,6 +1551,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>         opts.switch_branch_doing_nothing_is_ok = 0;\n>         opts.only_merge_on_switching_branches = 1;\n>         opts.implicit_detach = 0;\n> +       opts.overlay_mode = -1;\n>\n>         options = parse_options_dup(switch_options);\n>         options = add_common_options(&opts, options);\n> @@ -1569,6 +1570,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n>         struct option restore_options[] = {\n>                 OPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n>                            N_(\"where the checkout from\")),\n> +               OPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n>                 OPT_END()\n>         };\n>         int ret;\n> @@ -1577,6 +1579,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n>         opts.accept_ref = 0;\n>         opts.accept_pathspec = 1;\n>         opts.empty_pathspec_ok = 0;\n> +       opts.overlay_mode = 0;\n>\n>         options = parse_options_dup(restore_options);\n>         options = add_common_options(&opts, options);\n> --\n> 2.21.0.rc1.337.gdf7f8d0522\n\nThe rest all looks good.\n"},{"id":"371045","messageId":"CABPp-BFv-a3Uw1g+ebLqTHRbCedsv1akZxxJ7QfeyXtXBdQuOw@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-7-pclouds@gmail.com","subject":"Re: [PATCH v1 06/11] restore: add --worktree and --index","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:52:02Z","receivedAt":"2019-03-09T18:52:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> 'git checkout <tree-ish> <pathspec>' updates both index and\n> worktree. But updating the index when you want to restore worktree\n> files is non-intuitive. The index contains the data ready for the next\n> commit, and there's no indication that the user will want to commit\n> the restored versions.\n>\n> 'git restore' therefore by default only touches worktree. The user has\n> the option to update either the index with\n>\n>     git restore --source=<tree> --index <path>  (1)\n>\n> or update both with\n>\n>     git restore --source=<tree> --index --worktree <path> (2)\n>\n> PS. Orignally I wanted to make worktree update default and form (1)\n> would add index update while also updating the worktree, and the user\n> would need to do \"--index --no-worktree\" to update index only. But it\n> looks really confusing that \"--index\" option alone updates both. So\n> now form (2) is used for both, which reads much more obvious.\n>\n> PPS. Yes form (1) overlaps with \"git reset <rev> <path>\". I don't know\n> if we can ever turn \"git reset\" back to \"_always_ reset HEAD and\n> optionally do something else\".\n\nI'm really happy with how this series is going generally.  :-)\n\n> +       if (!opts->checkout_worktree && !opts->checkout_index)\n> +               die(_(\"neither '%s' or '%s' is specified\"),\n> +                   \"--index\", \"--worktree\");\n\nIs this die() or BUG()?  I thought --worktree was the default.\n\n> +               else\n> +                       die(_(\"'%s' with only '%s' is not currently supported\"),\n> +                           \"--patch\", \"--worktree\");\n\n:-(\n\n> +               /*\n> +                * NEEDSWORK: if --worktree is not specified, we\n> +                * should save stat info of checked out files in the\n> +                * index to avoid the next (potentially costly)\n> +                * refresh. But it's a bit tricker to do...\n> +                */\n> +               rollback_lock_file(&lock_file);\n\nA total tangent: I see both FIXME and NEEDSWORK in the codebase.  Are\nthere other 'keywords' of this type that we use?  Is there a\npreference for how they are used?\n"},{"id":"371046","messageId":"CABPp-BEW3NBBoVrpZWiopgKnFZAK7RCZ5mAzdjeSN7wAPoLm2g@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-8-pclouds@gmail.com","subject":"Re: [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T18:58:44Z","receivedAt":"2019-03-09T18:58:57Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> \"git restore --index\" does not make much sense since we're told to\n> restore the index from the (by default) index. Set default source to\n> HEAD in this case. This is basically the same as \"git reset -- <paths>\".\n\nCan I suggest a slight rewording to the commit message?\n\n\"git restore --index\" without --source does not make much sense since by\ndefault we restore from the index.  Instead of copying the index to\nitself, set the default source to HEAD in this case, yielding behavior\nthat matches \"git reset -- <paths>\".\n\n\nThe rest looks good to me.\n"},{"id":"371047","messageId":"CABPp-BGg6k_=zjZ+vCGzh2hu5KnXf3=OMDGJ=eei0xmxh6trAQ@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-9-pclouds@gmail.com","subject":"Re: [PATCH v1 08/11] restore: support --patch","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T19:02:56Z","receivedAt":"2019-03-09T19:03:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> git-restore is different from git-checkout that it only restores the\n> worktree by default, not both worktree and index. add--interactive\n> needs some update to support this mode.\n\nNice!  That removes my frowny face from a few patches before...  :-)\n\n> @@ -436,9 +436,10 @@ static int checkout_paths(const struct checkout_opts *opts,\n>                         patch_mode = \"--patch=checkout\";\n>                 else if (opts->checkout_index && !opts->checkout_worktree)\n>                         patch_mode = \"--patch=reset\";\n> +               else if (!opts->checkout_index && opts->checkout_worktree)\n> +                       patch_mode = \"--patch=worktree\";\n>                 else\n> -                       die(_(\"'%s' with only '%s' is not currently supported\"),\n> -                           \"--patch\", \"--worktree\");\n> +                       BUG(\"either flag must have been set\");\n\nVery minor nit: I suspect we'll never hit this, but do we want to make\nthe error message slightly more descriptive (\"which flags\"?) in case\nsomeone doing refactoring work does?\n"},{"id":"371048","messageId":"CABPp-BGbqYUMEK0V_m0i7dpFb38Mm3sS-h0Ut-GSdvsKEZzRQg@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-11-pclouds@gmail.com","subject":"Re: [PATCH v1 10/11] completion: support restore","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T19:16:59Z","receivedAt":"2019-03-09T19:20:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Completion for restore is straightforward. We could still do better\n> though by give the list of just tracked files instead of all present\n> ones. But let's leave it for later.\n\ns/give/giving/\n\nI'm slightly worried that due to using --no-overlay mode by default in\nrestore, having tab-completion include untracked files increases the\nrisk of accidentally nuking the wrong file.  restore is a destructive\ncommand anyway and should thus be used with care, so perhaps this\nisn't a big deal, but I thought I'd mention it.\n"},{"id":"371049","messageId":"CABPp-BEVMc+k7fTtZqqAf=WdpFi3W-Tcjw+Ah57bcetOXVwxkQ@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-12-pclouds@gmail.com","subject":"Re: [PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-09T19:37:32Z","receivedAt":"2019-03-09T19:37:46Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> The new command \"git restore\" (together with \"git switch\") are added\n> to avoid the confusion of one-command-do-all \"git checkout\" for new\n> users. They are also helpful to avoid ambiguation context.\n\ns/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?\n\n> For these reasons, promote it everywhere possible. This includes\n> documentation, suggestions/advice from other commands.\n>\n> One nice thing about git-restore is the ability to restore\n> \"everything\", so it can be used in \"git status\" advice instead of both\n> \"git checkout\" and \"git reset\".  The three commands suggested by \"git\n> status\" are add, rm and restore.\n>\n> \"git checkout\" is also removed from \"git help\" (i.e. it's no longer\n> considered a commonly used command)\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\n> @@ -56,7 +56,7 @@ summary of what is included by any of the above for the next\n>  commit by giving the same set of parameters (options and paths).\n>\n>  If you make a commit and then find a mistake immediately after\n> -that, you can recover from it with 'git reset'.\n> +that, you can recover from it with 'git restore' or 'git reset'.\n\nDoes 'git revert' also belong in this set?  And do we need to extend\nour blurb differentiating reset and restore to also include revert?\n\n> @@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to\n>  throw away all uncommitted changes in your working directory, you\n>  should see linkgit:git-reset[1], particularly the `--hard` option.  If\n>  you want to extract specific files as they were in another commit, you\n> -should see linkgit:git-checkout[1], specifically the `git checkout\n> -<commit> -- <filename>` syntax.  Take care with these alternatives as\n> +should see linkgit:git-restore[1], specifically the `--source`\n> +option  Take care with these alternatives as\n>  both will discard uncommitted changes in your working directory.\n\nMissing period after \"option\".\n\n>  -------------------------------------------------\n> -$ git reset --hard HEAD\n> +$ git merge --abort\n\nNice additional cleanup, but should it be in a separate patch since it\nisn't about the restore command?\n\n\nElijah\n"},{"id":"371063","messageId":"CAPig+cTdE+mTtvExHm1Fm1SU=YkHai-d6BW6jJ9SGOWS3eZZPw@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-3-pclouds@gmail.com","subject":"Re: [PATCH v1 02/11] restore: take tree-ish from --source option instead","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-10T07:58:54Z","receivedAt":"2019-03-10T07:59:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 8, 2019 at 5:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This is another departure from 'git checkout' syntax, which uses -- to\n> separate ref and pathspec. The observation is restore (or \"git\n> checkout ,, <pathspec>\") is most often used to restore some files from\n\nWhat is the \",,\" thing?\n\n> the index. If this is correct, we can simplify it by taking a way the\n\ns/a way/away/\n\n> ref, so that we can write\n>\n>     git restore some-file\n>\n> without worrying about some-file being a ref and whether we need to do\n>\n>     git restore -- some-file\n>\n> for safety. If the source of the restore comes from a tree, it will be\n> in the form of an option with value, e.g.\n>\n>     git restore --source=this-tree some-file\n>\n> This is of course longer to type than using \"--\". But hopefully it\n> will not be used as often, and it is clearly easier to understand.\n>\n> dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\n> because it's irrelevant because we don't really care about dwim-ing.\n> With accept_ref being unset, dwim can't happen.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"},{"id":"371074","messageId":"CACsJy8BB1GAaBX=YcmNf1fLj5chc5hcvHLTwvzwJgvSWWKNFdQ@mail.gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"Re: [PATCH v1 00/11] And new command \"restore\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-10T11:19:28Z","receivedAt":"2019-03-10T11:19:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> - --index has a different meaning in git-apply. I don't have a good\n>   suggestion except \"yeah we have two different worlds now, the old\n>   and the new one\"\n\nI will rename --index to --staged to avoid this. It is already used as\nsynonym for --cached in git-diff. I will also add --staged as synonym\nto \"git rm\" so that \"git status\" can consistently advise \"git <verb>\n--staged\" where verb is either restore or rm.\n\nSince option rename is a lot of work. If you think this is not a good\nmove, raise it now, even if it's just \"I don't like it, no reasons\".\n-- \nDuy\n"},{"id":"371086","messageId":"CAPig+cQ8YqAJ=3T++FCpew1SAUnwPYzZ3wRwExGu8Ed97ELrnw@mail.gmail.com","threadId":"50690","inReplyTo":"CABPp-BFv-a3Uw1g+ebLqTHRbCedsv1akZxxJ7QfeyXtXBdQuOw@mail.gmail.com","subject":"Re: [PATCH v1 06/11] restore: add --worktree and --index","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-10T20:03:07Z","receivedAt":"2019-03-10T20:03:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Mar 9, 2019 at 1:52 PM Elijah Newren <newren@gmail.com> wrote:\n> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > +       if (!opts->checkout_worktree && !opts->checkout_index)\n> > +               die(_(\"neither '%s' or '%s' is specified\"),\n> > +                   \"--index\", \"--worktree\");\n>\n> Is this die() or BUG()?  I thought --worktree was the default.\n\nThe user might type \"git restore --no-worktree --no-index\" which would\ntrigger this error message, so definitely die(), not a BUG().\n"},{"id":"371088","messageId":"6B5DF0A1-DCEB-45C2-B205-E3CF7469B7C6@gmail.com","threadId":"50690","inReplyTo":"CACsJy8BB1GAaBX=YcmNf1fLj5chc5hcvHLTwvzwJgvSWWKNFdQ@mail.gmail.com","subject":"Re: [PATCH v1 00/11] And new command \"restore\"","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2019-03-10T22:45:34Z","receivedAt":"2019-03-10T22:45:49Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"\n\nOn March 10, 2019 4:19:28 AM PDT, Duy Nguyen <pclouds@gmail.com> wrote:\n>On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>wrote:\n>> - --index has a different meaning in git-apply. I don't have a good\n>>   suggestion except \"yeah we have two different worlds now, the old\n>>   and the new one\"\n>\n>I will rename --index to --staged to avoid this. It is already used as\n>synonym for --cached in git-diff. I will also add --staged as synonym\n>to \"git rm\" so that \"git status\" can consistently advise \"git <verb>\n>--staged\" where verb is either restore or rm.\n>\n>Since option rename is a lot of work. If you think this is not a good\n>move, raise it now, even if it's just \"I don't like it, no reasons\".\n\nFor what it's worth, I think this is a good rename.\n\nThanks,\nJake\n\n-- \nSent from my Android device with K-9 Mail. Please excuse my brevity.\n"},{"id":"371148","messageId":"000b01d4d814$64907000$2db15000$@nexbridge.com","threadId":"50690","inReplyTo":"CABPp-BEVMc+k7fTtZqqAf=WdpFi3W-Tcjw+Ah57bcetOXVwxkQ@mail.gmail.com","subject":"RE: [PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Randall S. Becker","fromEmail":"rsbecker@nexbridge.com","sentAt":"2019-03-11T14:11:54Z","receivedAt":"2019-03-11T14:12:06Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"> -----Original Message-----\n> From: git-owner@vger.kernel.org <git-owner@vger.kernel.org> On Behalf\n> Of Elijah Newren\n> Sent: March 9, 2019 14:38\n> To: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> Cc: Git Mailing List <git@vger.kernel.org>; Junio C Hamano\n> <gitster@pobox.com>\n> Subject: Re: [PATCH v1 11/11] doc: promote \"git restore\"\n> \n> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy\n> <pclouds@gmail.com> wrote:\n> >\n> > The new command \"git restore\" (together with \"git switch\") are added\n> > to avoid the confusion of one-command-do-all \"git checkout\" for new\n> > users. They are also helpful to avoid ambiguation context.\n> \n> s/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?\n> \n> > For these reasons, promote it everywhere possible. This includes\n> > documentation, suggestions/advice from other commands.\n> >\n> > One nice thing about git-restore is the ability to restore\n> > \"everything\", so it can be used in \"git status\" advice instead of both\n> > \"git checkout\" and \"git reset\".  The three commands suggested by \"git\n> > status\" are add, rm and restore.\n> >\n> > \"git checkout\" is also removed from \"git help\" (i.e. it's no longer\n> > considered a commonly used command)\n\nJust curious: At what point did git checkout become not commonly used? Git switch and git restore are not yet in git as of 2.21.0, which is rather current. Maybe I missed a thread.\n\n"},{"id":"371151","messageId":"CACsJy8CJ5VimEJsJMN4G2sFxn7X72qM-8QLBo7NNSjWx39npug@mail.gmail.com","threadId":"50690","inReplyTo":"000b01d4d814$64907000$2db15000$@nexbridge.com","subject":"Re: [PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T14:36:58Z","receivedAt":"2019-03-11T14:37:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 9:12 PM Randall S. Becker\n<rsbecker@nexbridge.com> wrote:\n>\n> > -----Original Message-----\n> > From: git-owner@vger.kernel.org <git-owner@vger.kernel.org> On Behalf\n> > Of Elijah Newren\n> > Sent: March 9, 2019 14:38\n> > To: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> > Cc: Git Mailing List <git@vger.kernel.org>; Junio C Hamano\n> > <gitster@pobox.com>\n> > Subject: Re: [PATCH v1 11/11] doc: promote \"git restore\"\n> >\n> > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy\n> > <pclouds@gmail.com> wrote:\n> > >\n> > > The new command \"git restore\" (together with \"git switch\") are added\n> > > to avoid the confusion of one-command-do-all \"git checkout\" for new\n> > > users. They are also helpful to avoid ambiguation context.\n> >\n> > s/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?\n> >\n> > > For these reasons, promote it everywhere possible. This includes\n> > > documentation, suggestions/advice from other commands.\n> > >\n> > > One nice thing about git-restore is the ability to restore\n> > > \"everything\", so it can be used in \"git status\" advice instead of both\n> > > \"git checkout\" and \"git reset\".  The three commands suggested by \"git\n> > > status\" are add, rm and restore.\n> > >\n> > > \"git checkout\" is also removed from \"git help\" (i.e. it's no longer\n> > > considered a commonly used command)\n>\n> Just curious: At what point did git checkout become not commonly used? Git switch and git restore are not yet in git as of 2.21.0, which is rather current. Maybe I missed a thread.\n\nI declare \"git checkout\" not commonly used the moment git-switch and\ngit-restore are released!\n\nMore seriously, this is an attempt to promote new commands. The\n\"commonly used\" here refers to the set of commands that show when you\nrun \"git help\" (they are called \"common\" in command-list.txt\nclassification). Since the people who type \"git help\" are mostly new\npeople, I think promoting the friendlier alternative is a good idea.\nAnd no neither switch or restore has been released. They are not even\nin 'pu' yet.\n-- \nDuy\n"},{"id":"371153","messageId":"CACsJy8DeGQ=GRnRByNQ18Npe5JEcRXBf2oK3Xg6uv44OCg_EBA@mail.gmail.com","threadId":"50690","inReplyTo":"CABPp-BGbqYUMEK0V_m0i7dpFb38Mm3sS-h0Ut-GSdvsKEZzRQg@mail.gmail.com","subject":"Re: [PATCH v1 10/11] completion: support restore","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T15:22:53Z","receivedAt":"2019-03-11T15:23:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >\n> > Completion for restore is straightforward. We could still do better\n> > though by give the list of just tracked files instead of all present\n> > ones. But let's leave it for later.\n>\n> s/give/giving/\n>\n> I'm slightly worried that due to using --no-overlay mode by default in\n> restore, having tab-completion include untracked files increases the\n> risk of accidentally nuking the wrong file.  restore is a destructive\n> command anyway and should thus be used with care, so perhaps this\n> isn't a big deal, but I thought I'd mention it.\n\nThis makes me think about my \"git restore :/\" example, which does not\nremove untracked files because its source is the index. But isn't it\ninconsistent that we only remove untracked files with --source and\nkeep them without? Will that just confuse people more? Or did I just\nforget to implement no-overlay mode for the index too?\n\nSide note, to enable overlay mode again in git restore, maybe \"git\nrestore --keep-untracked --source...\" will be better than \"git restore\n--overlay --source...\"\n-- \nDuy\n"},{"id":"371155","messageId":"CACsJy8ASZ2jcrk7jf+5p0yCk9bLy-8SJmQyEjG9TSULhd+GhaQ@mail.gmail.com","threadId":"50690","inReplyTo":"CACsJy8DeGQ=GRnRByNQ18Npe5JEcRXBf2oK3Xg6uv44OCg_EBA@mail.gmail.com","subject":"Re: [PATCH v1 10/11] completion: support restore","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T15:39:57Z","receivedAt":"2019-03-11T15:40:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 10:22 PM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:\n> >\n> > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > >\n> > > Completion for restore is straightforward. We could still do better\n> > > though by give the list of just tracked files instead of all present\n> > > ones. But let's leave it for later.\n> >\n> > s/give/giving/\n> >\n> > I'm slightly worried that due to using --no-overlay mode by default in\n> > restore, having tab-completion include untracked files increases the\n> > risk of accidentally nuking the wrong file.  restore is a destructive\n> > command anyway and should thus be used with care, so perhaps this\n> > isn't a big deal, but I thought I'd mention it.\n>\n> This makes me think about my \"git restore :/\" example, which does not\n> remove untracked files because its source is the index. But isn't it\n> inconsistent that we only remove untracked files with --source and\n> keep them without? Will that just confuse people more? Or did I just\n> forget to implement no-overlay mode for the index too?\n\nNope you confused me. non-overlay mode never touches untracked files\nand so neither does git-restore.\n\nIt does make the description of git-restore about \"remove if paths do\nnot exist\" incorrect. But frankly I find it hard to explain\nnon-overlay mode with the index being the remove filter. In\ngit-checkout, where we update both index and worktree, this may make\nsense to use the index as the remove filter. But worktree works on the\nworktree only by default. I'll need to sleep on this.\n-- \nDuy\n"},{"id":"371158","messageId":"CABPp-BEmqJ4MfKwW81X21yXjZpf35kzqb9KaFs1uuBsmZ47_LA@mail.gmail.com","threadId":"50690","inReplyTo":"CACsJy8BB1GAaBX=YcmNf1fLj5chc5hcvHLTwvzwJgvSWWKNFdQ@mail.gmail.com","subject":"Re: [PATCH v1 00/11] And new command \"restore\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T16:01:47Z","receivedAt":"2019-03-11T16:02:01Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Mar 10, 2019 at 4:19 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > - --index has a different meaning in git-apply. I don't have a good\n> >   suggestion except \"yeah we have two different worlds now, the old\n> >   and the new one\"\n>\n> I will rename --index to --staged to avoid this. It is already used as\n> synonym for --cached in git-diff. I will also add --staged as synonym\n> to \"git rm\" so that \"git status\" can consistently advise \"git <verb>\n> --staged\" where verb is either restore or rm.\n>\n> Since option rename is a lot of work. If you think this is not a good\n> move, raise it now, even if it's just \"I don't like it, no reasons\".\n> --\n\nWorks for me, but this sounds like the kind of thing Junio might\nobject to, so I'm curious about his input.\n"},{"id":"371177","messageId":"CABPp-BEv1taYym_084qVJj3-jkWWS9hKXZ=grrmH7PDUb5ASwA@mail.gmail.com","threadId":"50690","inReplyTo":"CACsJy8ASZ2jcrk7jf+5p0yCk9bLy-8SJmQyEjG9TSULhd+GhaQ@mail.gmail.com","subject":"Re: [PATCH v1 10/11] completion: support restore","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T18:28:04Z","receivedAt":"2019-03-11T18:28:18Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"// CC'ing Thomas since this touches on a thread elsewhere about the glossary\n\nOn Mon, Mar 11, 2019 at 8:40 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Mon, Mar 11, 2019 at 10:22 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:\n> > >\n> > > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > > >\n> > > > Completion for restore is straightforward. We could still do better\n> > > > though by give the list of just tracked files instead of all present\n> > > > ones. But let's leave it for later.\n> > >\n> > > s/give/giving/\n> > >\n> > > I'm slightly worried that due to using --no-overlay mode by default in\n> > > restore, having tab-completion include untracked files increases the\n> > > risk of accidentally nuking the wrong file.  restore is a destructive\n> > > command anyway and should thus be used with care, so perhaps this\n> > > isn't a big deal, but I thought I'd mention it.\n> >\n> > This makes me think about my \"git restore :/\" example, which does not\n> > remove untracked files because its source is the index. But isn't it\n> > inconsistent that we only remove untracked files with --source and\n> > keep them without? Will that just confuse people more? Or did I just\n> > forget to implement no-overlay mode for the index too?\n>\n> Nope you confused me. non-overlay mode never touches untracked files\n> and so neither does git-restore.\n>\n> It does make the description of git-restore about \"remove if paths do\n> not exist\" incorrect. But frankly I find it hard to explain\n> non-overlay mode with the index being the remove filter. In\n> git-checkout, where we update both index and worktree, this may make\n> sense to use the index as the remove filter. But worktree works on the\n> worktree only by default. I'll need to sleep on this.\n\nSo, I guess this means that my addendum to Thomas' glossary entry for\noverlay is wrong; instead of:\n\n[[def_overlay]]overlay::\n       Only update and add files to the working directory, but don't\n       delete them, similar to how 'cp -R' works.  This is the default\n       in a <<def_checkout,checkout>>.  In contrast, no-overlay mode\n       will also delete files not present in the source, similar to\n       'rsync --delete'.\n\nthe definition should be (note the insertion of 'tracked'):\n\n[[def_overlay]]overlay::\n       Only update and add files to the working directory, but don't\n       delete them, similar to how 'cp -R' works.  This is the default\n       in a <<def_checkout,checkout>>.  In contrast, no-overlay mode\n       will also delete tracked files not present in the source,\n       similar to 'rsync --delete'.\n\n...and perhaps the reason this makes it hard for you to explain when\nthe index is the source is because this definition means there is no\ndifference between overlay and non-overlay mode for that specific\ncase.\n\n\nAm I on the right page now?\n"},{"id":"371331","messageId":"xmqq7ed3e5gj.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"CACsJy8CJ5VimEJsJMN4G2sFxn7X72qM-8QLBo7NNSjWx39npug@mail.gmail.com","subject":"Re: [PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-13T04:58:04Z","receivedAt":"2019-03-13T04:58:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> Just curious: At what point did git checkout become not commonly\n>> used? Git switch and git restore are not yet in git as of 2.21.0,\n>> which is rather current. Maybe I missed a thread.\n>\n> I declare \"git checkout\" not commonly used the moment git-switch and\n> git-restore are released!\n>\n> More seriously, this is an attempt to promote new commands. The\n> \"commonly used\" here refers to the set of commands that show when you\n> run \"git help\" (they are called \"common\" in command-list.txt\n> classification). Since the people who type \"git help\" are mostly new\n> people, I think promoting the friendlier alternative is a good idea.\n\nSo it is mainly how the word \"common\" is defined?  It's not how\ncommon the command is in the minds of the population of Git users,\nbut how common we want it to be?\n"},{"id":"371336","messageId":"nycvar.QRO.7.76.6.1903131005180.41@tvgsbejvaqbjf.bet","threadId":"50690","inReplyTo":"20190308101655.9767-10-pclouds@gmail.com","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-13T09:13:39Z","receivedAt":"2019-03-13T09:14:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Fri, 8 Mar 2019, Nguyễn Thái Ngọc Duy wrote:\n\n> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\n> new file mode 100755\n> index 0000000000..df91bf54bc\n> --- /dev/null\n> +++ b/t/t2070-restore.sh\n> @@ -0,0 +1,77 @@\n> +#!/bin/sh\n> +\n> +test_description='restore basic functionality'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\ttest_commit first &&\n> +\techo first-and-a-half >>first.t &&\n> +\tgit add first.t &&\n> +\ttest_commit second &&\n> +\techo one >one &&\n> +\techo two >two &&\n> +\techo untracked >untracked &&\n> +\techo ignored >ignored &&\n> +\techo /ignored >.gitignore &&\n> +\tgit add one two .gitignore &&\n> +\tgit update-ref refs/heads/one master\n> +'\n> +\n> +test_expect_success 'restore without pathspec is not ok' '\n> +\ttest_must_fail git restore &&\n> +\ttest_must_fail git restore --source=first\n> +'\n> +\n> +test_expect_success 'restore -p without pathspec is fine' '\n> +\techo q >cmd &&\n> +\tgit restore -p <cmd\n> +'\n\nThis breaks with NO_PERL builds. See e.g.\n\nhttps://dev.azure.com/gitgitgadget/git/_build/results?buildId=4581\nhttps://dev.azure.com/gitgitgadget/git/_build/results?buildId=4584\nhttps://dev.azure.com/git/git/_build/results?buildId=386\n\nYou need this squashed in:\n\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nindex df91bf54bc06..f4766544c5de 100755\n--- a/t/t2070-restore.sh\n+++ b/t/t2070-restore.sh\n@@ -23,7 +23,7 @@ test_expect_success 'restore without pathspec is not ok'\n'\n \ttest_must_fail git restore --source=first\n '\n \n-test_expect_success 'restore -p without pathspec is fine' '\n+test_expect_success PERL 'restore -p without pathspec is fine' '\n \techo q >cmd &&\n \tgit restore -p <cmd\n '\n\n\nJunio, could you please add that as SQUASH??? on top of\nnd/switch-and-restore to unbreak the CI builds?\n\nDuy, have you thought about making use of the CI builds? You could catch\nthose bugs before they hit the Git mailing list...\n\nThanks,\nDscho"},{"id":"371337","messageId":"nycvar.QRO.7.76.6.1903131014380.41@tvgsbejvaqbjf.bet","threadId":"50690","inReplyTo":"20190308101655.9767-2-pclouds@gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-13T09:17:54Z","receivedAt":"2019-03-13T09:18:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Fri, 8 Mar 2019, Nguyễn Thái Ngọc Duy wrote:\n\n> diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\n> new file mode 100644\n> index 0000000000..a667a5ced4\n> --- /dev/null\n> +++ b/Documentation/git-restore.txt\n> @@ -0,0 +1,172 @@\n> [...]\n> +\n> +To restore all files in the current directory\n> +\n> +------------\n> +$ git restore .\n> +------------\n> +\n> +or to restore all working tree files with 'top' pathspec magic (see\n> +linkgit::gitglossary[7])\n\nThis has an extra colon, breaking the build:\n\nhttps://dev.azure.com/git/git/_build/results?buildId=386\n\nYou need this squashed in:\n\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex a667a5ced4ec..1faeee50593e 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -144,7 +144,7 @@ $ git restore .\n ------------\n \n or to restore all working tree files with 'top' pathspec magic (see\n-linkgit::gitglossary[7])\n+linkgit:gitglossary[7])\n \n ------------\n $ git restore :/\n\nJunio, could you please put this as a SQUASH??? on top of\nnd/switch-and-restore, to unbreak the CI builds?\n\nThanks,\nDscho"},{"id":"371338","messageId":"CACsJy8Ce3Sn7sWDOqGPeOk7JiXg+3KSaox4PFZxfh90B6E81_Q@mail.gmail.com","threadId":"50690","inReplyTo":"nycvar.QRO.7.76.6.1903131005180.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-13T09:20:42Z","receivedAt":"2019-03-13T09:21:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Duy, have you thought about making use of the CI builds? You could catch\n> those bugs before they hit the Git mailing list...\n\nUsing Azure? No.\n-- \nDuy\n"},{"id":"371340","messageId":"CACsJy8DBRMQq9fAepLkTQbvzhx2U3X2wdhv4xzPXyCnGxZSo2A@mail.gmail.com","threadId":"50690","inReplyTo":"CABPp-BFv-a3Uw1g+ebLqTHRbCedsv1akZxxJ7QfeyXtXBdQuOw@mail.gmail.com","subject":"Re: [PATCH v1 06/11] restore: add --worktree and --index","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-13T10:02:34Z","receivedAt":"2019-03-13T10:03:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Mar 10, 2019 at 1:52 AM Elijah Newren <newren@gmail.com> wrote:\n> > +               /*\n> > +                * NEEDSWORK: if --worktree is not specified, we\n> > +                * should save stat info of checked out files in the\n> > +                * index to avoid the next (potentially costly)\n> > +                * refresh. But it's a bit tricker to do...\n> > +                */\n> > +               rollback_lock_file(&lock_file);\n>\n> A total tangent: I see both FIXME and NEEDSWORK in the codebase.  Are\n> there other 'keywords' of this type that we use?  Is there a\n> preference for how they are used?\n\nI don't think so. I've seen FIXME, NEEDSWORK, TODO and XXX. I think\nit's often up to the author to pick one. We could unify and use just\none keyword, which helps spot these. But I don't think we keep future\nother keywords out long term. This seems to pick up most of them with\na couple false positives\n\ngit grep '^\\s*\\* [A-Z]\\+: ' \\*.c\n-- \nDuy\n"},{"id":"371408","messageId":"nycvar.QRO.7.76.6.1903132316220.41@tvgsbejvaqbjf.bet","threadId":"50690","inReplyTo":"CACsJy8Ce3Sn7sWDOqGPeOk7JiXg+3KSaox4PFZxfh90B6E81_Q@mail.gmail.com","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-13T22:17:50Z","receivedAt":"2019-03-13T22:17:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Wed, 13 Mar 2019, Duy Nguyen wrote:\n\n> On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> > Duy, have you thought about making use of the CI builds? You could catch\n> > those bugs before they hit the Git mailing list...\n> \n> Using Azure? No.\n\nThis hostility is totally unnecessary. Especially since we still have\nTravis builds that you could also use to catch your bugs early, so that I\nwould not have to catch them.\n\nYou could also work on setting up Circle CI, or whatever else you have in\nmind. As long as you catch your bugs early.\n\nThanks,\nJohannes\n"},{"id":"371411","messageId":"xmqqva0mcs5o.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"CABPp-BFv-a3Uw1g+ebLqTHRbCedsv1akZxxJ7QfeyXtXBdQuOw@mail.gmail.com","subject":"Re: [PATCH v1 06/11] restore: add --worktree and --index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-13T22:42:59Z","receivedAt":"2019-03-13T22:43:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n>> +               /*\n>> +                * NEEDSWORK: if --worktree is not specified, we\n>> +                * should save stat info of checked out files in the\n>> +                * index to avoid the next (potentially costly)\n>> +                * refresh. But it's a bit tricker to do...\n>> +                */\n>> +               rollback_lock_file(&lock_file);\n>\n> A total tangent: I see both FIXME and NEEDSWORK in the codebase.\n> Are there other 'keywords' of this type that we use?  Is there a\n> preference for how they are used?\n\nIf it makes it simpler, I can easily declare that NEEDSWORK is the\npreferred one (I do not think I ever wrote anything else) to avoid\nwasting list bandwidth ;-)\n\n"},{"id":"371414","messageId":"xmqqr2bacrgf.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"CACsJy8BB1GAaBX=YcmNf1fLj5chc5hcvHLTwvzwJgvSWWKNFdQ@mail.gmail.com","subject":"Re: [PATCH v1 00/11] And new command \"restore\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-13T22:58:08Z","receivedAt":"2019-03-13T22:58:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> - --index has a different meaning in git-apply. I don't have a good\n>>   suggestion except \"yeah we have two different worlds now, the old\n>>   and the new one\"\n>\n> I will rename --index to --staged to avoid this. It is already used as\n> synonym for --cached in git-diff. I will also add --staged as synonym\n> to \"git rm\" so that \"git status\" can consistently advise \"git <verb>\n> --staged\" where verb is either restore or rm.\n\nDoes \"restore --index\" (now \"--staged\") work the same way as\n\"--cached\" aka \"--staged\" in git-diff?  \"--cached\" is about\naffecting only what is in the index (as opposed to \"--index\"\naffecting both the index and the working tree).\n\nIf it does, then calling it \"--cached\" aka \"--staged\" does make more\nsense than calling it \"--index\", I would think.  Otherwise, such a\nrename does not buy us much.\n"},{"id":"371450","messageId":"CACsJy8D+bbDR4Oavf_aTk=rN2JDOQ5nhP5ZgCmML66xkMf8fYw@mail.gmail.com","threadId":"50690","inReplyTo":"xmqqr2bacrgf.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v1 00/11] And new command \"restore\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T03:49:11Z","receivedAt":"2019-03-14T03:49:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 14, 2019 at 5:58 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> > On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >> - --index has a different meaning in git-apply. I don't have a good\n> >>   suggestion except \"yeah we have two different worlds now, the old\n> >>   and the new one\"\n> >\n> > I will rename --index to --staged to avoid this. It is already used as\n> > synonym for --cached in git-diff. I will also add --staged as synonym\n> > to \"git rm\" so that \"git status\" can consistently advise \"git <verb>\n> > --staged\" where verb is either restore or rm.\n>\n> Does \"restore --index\" (now \"--staged\") work the same way as\n> \"--cached\" aka \"--staged\" in git-diff?  \"--cached\" is about\n> affecting only what is in the index (as opposed to \"--index\"\n> affecting both the index and the working tree).\n\nYes. To affect both you would need --staged --worktree (or -WS for\nshort because I don't think anybody is going to type that many\ncharacters)\n\n> If it does, then calling it \"--cached\" aka \"--staged\" does make more\n> sense than calling it \"--index\", I would think.  Otherwise, such a\n> rename does not buy us much.\n-- \nDuy\n"},{"id":"371456","messageId":"CACsJy8CbGBFr0jknuWrkrS19OJhpPaUDwHa8-+j1Ak=DQkmpdw@mail.gmail.com","threadId":"50690","inReplyTo":"nycvar.QRO.7.76.6.1903132316220.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T04:57:17Z","receivedAt":"2019-03-14T04:57:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 14, 2019 at 5:17 AM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> Hi Duy,\n>\n> On Wed, 13 Mar 2019, Duy Nguyen wrote:\n>\n> > On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin\n> > <Johannes.Schindelin@gmx.de> wrote:\n> > > Duy, have you thought about making use of the CI builds? You could catch\n> > > those bugs before they hit the Git mailing list...\n> >\n> > Using Azure? No.\n>\n> This hostility is totally unnecessary. Especially since we still have\n> Travis builds that you could also use to catch your bugs early, so that I\n> would not have to catch them.\n\nThanks. I'll check back on github in a couple years. Adding CI to\ngitlab is on my todo list but it's very low.\n-- \nDuy\n"},{"id":"371460","messageId":"xmqq5zsm9ffv.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"20190308101655.9767-10-pclouds@gmail.com","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-14T05:45:56Z","receivedAt":"2019-03-14T05:46:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\n> new file mode 100755\n> index 0000000000..df91bf54bc\n> --- /dev/null\n> +++ b/t/t2070-restore.sh\n> @@ -0,0 +1,77 @@\n> +#!/bin/sh\n> +\n> +test_description='restore basic functionality'\n> +\n> +. ./test-lib.sh\n> + ...\n> +test_expect_success 'restore -p without pathspec is fine' '\n> +\techo q >cmd &&\n> +\tgit restore -p <cmd\n> +'\n\nThis stands out as a sore thumb, being an invocation with '-p' while\nall the other tests for the '-p' feature are in t2071.\n\nShoudln't it move to somewhere near the beginning of 2071?\n\n> diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\n> new file mode 100755\n> index 0000000000..46ebcb2413\n> --- /dev/null\n> +++ b/t/t2071-restore-patch.sh\n"},{"id":"371462","messageId":"xmqq1s3a9ezx.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"xmqq5zsm9ffv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v1 09/11] t: add tests for restore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-14T05:55:30Z","receivedAt":"2019-03-14T05:55:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +test_expect_success 'restore -p without pathspec is fine' '\n>> +\techo q >cmd &&\n>> +\tgit restore -p <cmd\n>> +'\n>\n> This stands out as a sore thumb, being an invocation with '-p' while\n> all the other tests for the '-p' feature are in t2071.\n\nI'll have this inserted immediately after 09/11 in the meantime.\n\nI wonder if there is a clean way to make everything in 2071 with\nPERL prereq.  I generally do not like the pattern used by some tests\nthat manually check the prererq and have test_done early, though.\n\nThanks.\n\n-- >8 --\nFrom: Junio C Hamano <gitster@pobox.com>\nDate: Thu, 14 Mar 2019 14:48:47 +0900\nSubject: [PATCH] SQUASH??? move -p test to 2071 from 2070\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t2070-restore.sh       | 5 -----\n t/t2071-restore-patch.sh | 5 +++++\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nindex df91bf54bc..3dc7032839 100755\n--- a/t/t2070-restore.sh\n+++ b/t/t2070-restore.sh\n@@ -23,11 +23,6 @@ test_expect_success 'restore without pathspec is not ok' '\n \ttest_must_fail git restore --source=first\n '\n \n-test_expect_success 'restore -p without pathspec is fine' '\n-\techo q >cmd &&\n-\tgit restore -p <cmd\n-'\n-\n test_expect_success 'restore a file, ignoring branch of same name' '\n \tcat one >expected &&\n \techo dirty >>one &&\ndiff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\nindex 46ebcb2413..98b2476e7c 100755\n--- a/t/t2071-restore-patch.sh\n+++ b/t/t2071-restore-patch.sh\n@@ -16,6 +16,11 @@ test_expect_success PERL 'setup' '\n \tsave_head\n '\n \n+test_expect_success PERL 'restore -p without pathspec is fine' '\n+\techo q >cmd &&\n+\tgit restore -p <cmd\n+'\n+\n # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'\n \n test_expect_success PERL 'saying \"n\" does nothing' '\n-- \n2.21.0-155-ge902e9bcae\n\n\n"},{"id":"372404","messageId":"20190325095322.GA1617@ash","threadId":"50690","inReplyTo":"CABPp-BFXZMorrHph3hGFnqfceHs68byWNgffNKGp1ov6X5-o5A@mail.gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-25T09:53:22Z","receivedAt":"2019-03-25T09:53:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"(Including Stefan, in case he's still interested in git and planned\nsomething for the \"submodule\" restore part I mention below)\n\nOn Fri, Mar 08, 2019 at 10:01:25AM -0800, Elijah Newren wrote:\n> Thanks for working on this; overall looks really good.  I've got a few\n> comments here and there on the wording and combinations of options...\n\nOK now that the 'git switch' part is mostly settled, I could go back\nto 'git restore'. I have a lot more homework to do before sending v2,\nbut how about this?\n\nMost of this is minor rephrasing and clarification (thanks!). The big\nchange is `--force` is split into `--keep-unmerged` and\n`--force-submodules`, the same direction I took in 'git switch'.\n\nSubmodules actually pose another problem because there are different\nthings in a submodule to restore:\n\n - restore HEAD only, leave submodule's worktree alone. This is\n   `--no-recurse-submodules` (default)\n \n - restoring `HEAD`, which is basically \"git switch --detach\n   <something>\" inside the submodule. This is `--recurse-submodules`.\n\n - restoring the paths inside the submodules, i.e. the actual \"git\n   restore <some paths>\" inside the submodule.\n\n - a combinarion of two. Basically you want \"git reset --hard\" in\n   every selected submodule.\n\nI wonder if we ever need to cover the third and forth case (and if so,\nwhat will the UI be like).  I suppose we could eventually make\n--recurse-submodules take an argument to specify what to restore,\nmaybe...\n\nPS. I just found https://asciidoclive.com/. Could be useful to paste\nthis text and render without going through git's build system.\n\n-- 8< --\ngit-restore(1)\n==============\n\nNAME\n----\ngit-restore - Restore working tree files\n\nSYNOPSIS\n--------\n[verse]\n'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...\n'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]\n\nDESCRIPTION\n-----------\nRestore specified paths in the working tree with some contents from a\nrestore source. If a path is tracked but does not exist in the restore\nsource, it will be removed to match the source.\n\nThe command can also be used to restore the content in the index with\n`--staged`, or restore both the working tree and the index with\n`--staged --worktree`.\n\nBy default, the restore sources for working tree and the index are the\nindex and `HEAD` respectively. `--source` could be used to specify a\ncommit as the restore source.\n\nA note about the differences between this command and linkgit:git-reset[1].\n`git restore` modifies the working tree (and maybe the index) to\nchange file content to match some other, usually older, version, but\ndoes not update your branch. `git reset` is mainly about moving the\ntip of your branch in order to either add or remove commits from the\nbranch.\n\nOPTIONS\n-------\n-s <tree>::\n--source=<tree>::\n\tRestore the working tree files with the content from the given\n\ttree. It is common to specify the source tree by naming a\n\tcommit, branch or tag associated with it.\n+\nIf not specified, the default restore source for the working tree is\nthe index, and index `HEAD`. When both `--staged` and `--worktree`\nare specified, `--source` must also be specified.\n\n-p::\n--patch::\n\tInteractively select hunks in the difference between the\n\trestore source and the restore location. See the ``Interactive\n\tMode'' section of linkgit:git-add[1] to learn how to operate\n\tthe `--patch` mode.\n+\nNote that `--patch` can accept no pathspec and will restore all\nmodified paths.\n\n-W::\n--worktree::\n-S::\n--staged::\n\tSpecify the restore location. If neither option is specified,\n\tby default the working tree is restored. Specifying `--staged`\n\twill only restore the index. Specifying both restores both.\n\n-q::\n--quiet::\n\tQuiet, suppress feedback messages.\n\n--progress::\n--no-progress::\n\tProgress status is reported on the standard error stream\n\tby default when it is attached to a terminal, unless `--quiet`\n\tis specified. This flag enables progress reporting even if not\n\tattached to a terminal, regardless of `--quiet`.\n\n-f::\n--force::\n\tAn alias of `--keep-unmerged` and `--force-submodules`.\n\n--keep-unmerged::\n\tIf `--source` is not specified, unmerged entries are left\n\talone and will not fail the operation.\n\n--force-submodules::\n\tContinue even if restoring `HEAD` of a submodule leads to loss\n\tof local changes in that submodule with `--recurse-submodules`.\n\n--ours::\n--theirs::\n\tRestore from stage #2 ('ours') or #3 ('theirs') for unmerged\n\tpaths.\n+\nNote that during `git rebase` and `git pull --rebase`, 'ours' and\n'theirs' may appear swapped. See the explanation of the same options\nin linkgit:git-checkout[1] for details.\n\n-m::\n--merge::\n\tRecreate the conflicted merge in the specified paths.\n\n--conflict=<style>::\n\tThe same as `--merge` option above, but changes the way the\n\tconflicting hunks are presented, overriding the merge.conflictStyle\n\tconfiguration variable.  Possible values are \"merge\" (default)\n\tand \"diff3\" (in addition to what is shown by \"merge\" style,\n\tshows the original contents).\n\n--ignore-skip-worktree-bits::\n\tIn sparse checkout mode, by default is to only update entries\n\tmatched by `<pathspec>` and sparse patterns in\n\t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n\tpatterns and unconditionally restores any files in\n\t`<pathspec>`.\n\n--recurse-submodules::\n--no-recurse-submodules::\n\tUsing `--recurse-submodules` will update the content of all\n\tinitialized submodules according to the commit recorded in the\n\tsuperproject. If nothing (or `--no-recurse-submodules`) is\n\tused, the work trees of submodules will not be updated.  Just\n\tlike linkgit:git-submodule[1], this will detach `HEAD` of the\n\tsubmodules.\n\n--overlay::\n--no-overlay::\n\tIn overlay mode, the command never removes files when\n\trestoring. In no-overlay mode, tracked files that appear in\n\tthe index and working tree, but not in `--source` tree are\n\tremoved, to make them match `<tree>` exactly. The default is\n\tno-overlay mode.\n\nEXAMPLES\n--------\n\nThe following sequence switches to the `master` branch, reverts the\n`Makefile` to two revisions back, deletes hello.c by mistake, and gets\nit back from the index.\n\n------------\n$ git switch master\n$ git restore --source master~2 Makefile  <1>\n$ rm -f hello.c\n$ git restore hello.c                     <2>\n------------\n\n<1> take a file out of another commit\n<2> restore hello.c from the index\n\nIf you want to restore _all_ C source files to match the version in\nthe index, you can say\n\n------------\n$ git restore '*.c'\n------------\n\nNote the quotes around `*.c`.  The file `hello.c` will also be\nrestored, even though it is no longer in the working tree, because the\nfile globbing is used to match entries in the index (not in the\nworking tree by the shell).\n\nTo restore all files in the current directory\n\n------------\n$ git restore .\n------------\n\nor to restore all working tree files with 'top' pathspec magic (see\nlinkgit:gitglossary[7])\n\n------------\n$ git restore :/\n------------\n\nTo restore a file in the index to match the version in `HEAD` (this is\nthe same as using linkgit:git-reset[1])\n\n------------\n$ git restore --staged hello.c\n------------\n\nor you can restore both the index and the working tree (this the same\nas using linkgit:git-checkout[1])\n\n------------\n$ git restore --source=HEAD --staged --worktree hello.c\n------------\n\nor the short form which is more practical but less readable:\n\n------------\n$ git restore -s@ -SW hello.c\n------------\n\nSEE ALSO\n--------\nlinkgit:git-checkout[1],\nlinkgit:git-reset[1]\n\nGIT\n---\nPart of the linkgit:git[1] suite\n-- 8< --\n"},{"id":"372436","messageId":"CABPp-BFtsQzDj3mCr1aRTpy2PQCzATjGGccECCmH8Qa_Y4aASQ@mail.gmail.com","threadId":"50690","inReplyTo":"20190325095322.GA1617@ash","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-25T15:51:04Z","receivedAt":"2019-03-25T15:51:20Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 25, 2019 at 2:53 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> (Including Stefan, in case he's still interested in git and planned\n> something for the \"submodule\" restore part I mention below)\n>\n> On Fri, Mar 08, 2019 at 10:01:25AM -0800, Elijah Newren wrote:\n> > Thanks for working on this; overall looks really good.  I've got a few\n> > comments here and there on the wording and combinations of options...\n>\n> OK now that the 'git switch' part is mostly settled, I could go back\n> to 'git restore'. I have a lot more homework to do before sending v2,\n> but how about this?\n>\n> Most of this is minor rephrasing and clarification (thanks!). The big\n> change is `--force` is split into `--keep-unmerged` and\n> `--force-submodules`, the same direction I took in 'git switch'.\n>\n> Submodules actually pose another problem because there are different\n> things in a submodule to restore:\n>\n>  - restore HEAD only, leave submodule's worktree alone. This is\n>    `--no-recurse-submodules` (default)\n>\n>  - restoring `HEAD`, which is basically \"git switch --detach\n>    <something>\" inside the submodule. This is `--recurse-submodules`.\n>\n>  - restoring the paths inside the submodules, i.e. the actual \"git\n>    restore <some paths>\" inside the submodule.\n>\n>  - a combinarion of two. Basically you want \"git reset --hard\" in\n>    every selected submodule.\n>\n> I wonder if we ever need to cover the third and forth case (and if so,\n> what will the UI be like).  I suppose we could eventually make\n> --recurse-submodules take an argument to specify what to restore,\n> maybe...\n\nThis looks pretty good to me, though I've got some small comments\nbelow.  Note that I'm basically glossing over or ignoring any part\ntouching on submodules, so I could easily miss even glaring wording\nproblems in that part.\n\n> PS. I just found https://asciidoclive.com/. Could be useful to paste\n> this text and render without going through git's build system.\n\nInteresting.  :-)\n\n> -- 8< --\n> git-restore(1)\n> ==============\n>\n> NAME\n> ----\n> git-restore - Restore working tree files\n>\n> SYNOPSIS\n> --------\n> [verse]\n> 'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...\n> 'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]\n>\n> DESCRIPTION\n> -----------\n> Restore specified paths in the working tree with some contents from a\n> restore source. If a path is tracked but does not exist in the restore\n> source, it will be removed to match the source.\n>\n> The command can also be used to restore the content in the index with\n> `--staged`, or restore both the working tree and the index with\n> `--staged --worktree`.\n>\n> By default, the restore sources for working tree and the index are the\n> index and `HEAD` respectively. `--source` could be used to specify a\n> commit as the restore source.\n>\n> A note about the differences between this command and linkgit:git-reset[1].\n> `git restore` modifies the working tree (and maybe the index) to\n> change file content to match some other, usually older, version, but\n> does not update your branch. `git reset` is mainly about moving the\n> tip of your branch in order to either add or remove commits from the\n> branch.\n\nMaybe include revert as well?  Perhaps something like...\n\nA note about the differences between this command, linkgit:git-reset[1],\nand and linkgit:git-revert[1].  `git restore` modifies the working tree\n(and maybe the index) to change file content to match some other,\nusually older, version, but does not update your branch. `git reset` is\nmainly about moving the tip of your branch to some other commit,\nimplicitly adding and/or removing many commits to the branch.  `git\nrevert` is about creating and adding new commits to your branch that\nreverse the changes made in specified commits.\n\n(There is also a similar description in git-revert, which also\ncompares all three of revert, reset, and checkout.  It's shorter, but\nfocuses more from the \"revert\" angle.)\n\n> OPTIONS\n> -------\n> -s <tree>::\n> --source=<tree>::\n>         Restore the working tree files with the content from the given\n>         tree. It is common to specify the source tree by naming a\n>         commit, branch or tag associated with it.\n> +\n> If not specified, the default restore source for the working tree is\n> the index, and index `HEAD`. When both `--staged` and `--worktree`\n> are specified, `--source` must also be specified.\n\ns/index `HEAD`/the default restore source for the index is `HEAD`/\n\n> -p::\n> --patch::\n>         Interactively select hunks in the difference between the\n>         restore source and the restore location. See the ``Interactive\n>         Mode'' section of linkgit:git-add[1] to learn how to operate\n>         the `--patch` mode.\n> +\n> Note that `--patch` can accept no pathspec and will restore all\n> modified paths.\n\ns/will restore all/will prompt to restore with all/\n\n> -W::\n> --worktree::\n> -S::\n> --staged::\n>         Specify the restore location. If neither option is specified,\n>         by default the working tree is restored. Specifying `--staged`\n>         will only restore the index. Specifying both restores both.\n>\n> -q::\n> --quiet::\n>         Quiet, suppress feedback messages.\n>\n> --progress::\n> --no-progress::\n>         Progress status is reported on the standard error stream\n>         by default when it is attached to a terminal, unless `--quiet`\n>         is specified. This flag enables progress reporting even if not\n>         attached to a terminal, regardless of `--quiet`.\n>\n> -f::\n> --force::\n>         An alias of `--keep-unmerged` and `--force-submodules`.\n>\n> --keep-unmerged::\n>         If `--source` is not specified, unmerged entries are left\n>         alone and will not fail the operation.\n\nThis isn't very clear to me.  Maybe:\n\nIncompatible with --source and --staged.  When specified, unmerged\nentries in the index will not fail the operation and the corresponding\nworking tree files will be left unmodified.\n\n> --force-submodules::\n>         Continue even if restoring `HEAD` of a submodule leads to loss\n>         of local changes in that submodule with `--recurse-submodules`.\n>\n> --ours::\n> --theirs::\n>         Restore from stage #2 ('ours') or #3 ('theirs') for unmerged\n>         paths.\n> +\n> Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> 'theirs' may appear swapped. See the explanation of the same options\n> in linkgit:git-checkout[1] for details.\n>\n> -m::\n> --merge::\n>         Recreate the conflicted merge in the specified paths.\n>\n> --conflict=<style>::\n>         The same as `--merge` option above, but changes the way the\n>         conflicting hunks are presented, overriding the merge.conflictStyle\n>         configuration variable.  Possible values are \"merge\" (default)\n>         and \"diff3\" (in addition to what is shown by \"merge\" style,\n>         shows the original contents).\n>\n> --ignore-skip-worktree-bits::\n>         In sparse checkout mode, by default is to only update entries\n>         matched by `<pathspec>` and sparse patterns in\n>         $GIT_DIR/info/sparse-checkout. This option ignores the sparse\n>         patterns and unconditionally restores any files in\n>         `<pathspec>`.\n>\n> --recurse-submodules::\n> --no-recurse-submodules::\n>         Using `--recurse-submodules` will update the content of all\n>         initialized submodules according to the commit recorded in the\n>         superproject. If nothing (or `--no-recurse-submodules`) is\n>         used, the work trees of submodules will not be updated.  Just\n>         like linkgit:git-submodule[1], this will detach `HEAD` of the\n>         submodules.\n>\n> --overlay::\n> --no-overlay::\n>         In overlay mode, the command never removes files when\n>         restoring. In no-overlay mode, tracked files that appear in\n>         the index and working tree, but not in `--source` tree are\n>         removed, to make them match `<tree>` exactly. The default is\n>         no-overlay mode.\n\nI think the \"index and working tree\" wording might cause some\nproblems.  What if the path doesn't exist in the source tree nor the\nworking tree, but does appear in the index and the user passes\n--staged?  Maybe just remove that phrase, e.g.:\n\n\"...In no-overlay mode, tracked files that do not appear in the\n`--source` tree are removed...\"\n\n?\n\n> EXAMPLES\n> --------\n>\n> The following sequence switches to the `master` branch, reverts the\n> `Makefile` to two revisions back, deletes hello.c by mistake, and gets\n> it back from the index.\n>\n> ------------\n> $ git switch master\n> $ git restore --source master~2 Makefile  <1>\n> $ rm -f hello.c\n> $ git restore hello.c                     <2>\n> ------------\n>\n> <1> take a file out of another commit\n> <2> restore hello.c from the index\n>\n> If you want to restore _all_ C source files to match the version in\n> the index, you can say\n>\n> ------------\n> $ git restore '*.c'\n> ------------\n>\n> Note the quotes around `*.c`.  The file `hello.c` will also be\n> restored, even though it is no longer in the working tree, because the\n> file globbing is used to match entries in the index (not in the\n> working tree by the shell).\n>\n> To restore all files in the current directory\n>\n> ------------\n> $ git restore .\n> ------------\n>\n> or to restore all working tree files with 'top' pathspec magic (see\n> linkgit:gitglossary[7])\n>\n> ------------\n> $ git restore :/\n> ------------\n>\n> To restore a file in the index to match the version in `HEAD` (this is\n> the same as using linkgit:git-reset[1])\n>\n> ------------\n> $ git restore --staged hello.c\n> ------------\n>\n> or you can restore both the index and the working tree (this the same\n> as using linkgit:git-checkout[1])\n>\n> ------------\n> $ git restore --source=HEAD --staged --worktree hello.c\n> ------------\n>\n> or the short form which is more practical but less readable:\n>\n> ------------\n> $ git restore -s@ -SW hello.c\n> ------------\n\n@ means HEAD?  I somehow missed this for five and half years.  (Glad\nyou put the \"less readable\" disclaimer/warning on this example,\nthough.)\n"},{"id":"373486","messageId":"CACsJy8DSyLttWr=QSg+enkTm4EKADmL99zZ0fiZRjCpXo9=5YA@mail.gmail.com","threadId":"50690","inReplyTo":"CABPp-BENtDUOpKYHZu=4rKFrQMaCE=fEtAWRqHvePjZYm1jTtg@mail.gmail.com","subject":"Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-09T12:18:20Z","receivedAt":"2019-04-09T12:18:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"(since 'git switch' is basically done, let's get back to 'git restore')\n\nOn Sun, Mar 10, 2019 at 1:27 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Sat, Mar 9, 2019 at 4:16 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> > > > +-q::\n> > > > +--quiet::\n> > > > +       Quiet, suppress feedback messages.\n> > > > +\n> > > > +--progress::\n> > > > +--no-progress::\n> > > > +       Progress status is reported on the standard error stream\n> > > > +       by default when it is attached to a terminal, unless `--quiet`\n> > > > +       is specified. This flag enables progress reporting even if not\n> > > > +       attached to a terminal, regardless of `--quiet`.\n> > >\n> > > I'm assuming this means there are feedback messages other than\n> > > progress feedback?\n> >\n> > There could be. This is carried over from git-checkout. I suspect this\n> > is about warnings that we print from time to time.\n>\n> Why would --quiet squelch warnings?  I figured it'd only squelch\n> feedback, informational, or progress messages.\n\nI'm wrong. Warnings are not suppressed by --quiet. It looks like\n--quiet is not really used by 'git restore'. Based on code inspection,\n--quiet implies --no-progress if --[no-]progress is not specified.\nOtherwise progress bar is enabled only when we have tty.\n\n> I understand you just carried it over from git-checkout, but as worded\n> it makes me wonder if checkout has suboptimal behavior or perhaps just\n> a suboptimal explanation of its flags ... and if it does, we probably\n> don't want to carry that over.\n\nBut there is something we don't show now, but I don't know if we should show.\n\nThe commit that started all this is 0f086e6dca (checkout: print\nsomething when checking out paths, 2018-11-13) where I tried to make\n'git checkout' mistakes easier to see by printing\n\n    Updated <n> paths from <some-checkout-source>\n\nBecause the reason is to help spot mistakes, this is only printed when\nyou do \"git checkout <paths>\" _without_ the double dashes to\ndisambiguate.\n\nSince git-restore has no ambiguation (between restoring paths and\nswitching branches) these messages are never printed. I'm just\nwondering if we should print them anyway. It gives some feedback\ninstead of the silent output in the successful case. Some might like\nit, some don't.\n\n> > > > +-f::\n> > > > +--force::\n> > > > +       If `--source` is not specified, unmerged entries are left alone\n> > > > +       and will not fail the operation. Unmerged entries are always\n> > > > +       replaced if `--source` is specified, regardless of `--force`.\n> > >\n> > > This may be slightly confusing, in particular it suggests that --index\n> > > (or --worktree and --index) are the default.  Is --force only useful\n> > > when --index is specified?  If it has utility with --worktree only,\n> > > what does it do?\n> >\n> > Well, this is 'git checkout -f' behavior which only concerns the\n> > index. So yeah it only matters with --index.\n>\n> Okay, good to know that this only matters with --index. However, new\n> problem: This makes the explanation feel contradictory, though,\n> because elsewhere you stated that --source=HEAD is implied when\n> --index is given without a source.  So, the combination of this\n> description and that fact suggests that -f is either useless (--index\n> is not specified) or ignored (because --source will either default to\n> HEAD or be specified by the user).  Maybe that's true and -f should be\n> removed from this new document.  If it has actual utility in some\n> cases, then this description needs to be reworked to explain what\n> those circiumstances are somehow.\n\nActually my description of this option is not clear. This is about\n--worktree, not --index.\n\nWhen we do \"git restore <path>\" (which by default implies --worktree),\nwe take the entry from the index and overwrite the worktree with it.\nBut if the entry is unmerged (and --ours or --theirs are not\nspecified), there is no good way we can handle it automatically.\n\nSo the default behavior in this case is abort the operation. -f goes\nwith a gentler option: ignore unmerged entries and go restore normal\nones. And to keep people informed that there _are_ unmerged entries,\nwarning is displayed for each one.\n\nSo, maybe I'll rename --force to --ignore-unmerged? This option will\nonly matter in the \"git restore [--worktree]\" form. In other words,\nrestoring worktree from the index. If --ignored-unmerged is specified\nin other modes, die().\n-- \nDuy\n"},{"id":"373627","messageId":"CACsJy8B2Mm=W56XAkVwtnPHPUCMpJcSdgrLxxKFYsS-q7r9a8Q@mail.gmail.com","threadId":"50690","inReplyTo":"xmqq7ed3e5gj.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v1 11/11] doc: promote \"git restore\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T10:55:31Z","receivedAt":"2019-04-11T10:55:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 13, 2019 at 11:58 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> >> Just curious: At what point did git checkout become not commonly\n> >> used? Git switch and git restore are not yet in git as of 2.21.0,\n> >> which is rather current. Maybe I missed a thread.\n> >\n> > I declare \"git checkout\" not commonly used the moment git-switch and\n> > git-restore are released!\n> >\n> > More seriously, this is an attempt to promote new commands. The\n> > \"commonly used\" here refers to the set of commands that show when you\n> > run \"git help\" (they are called \"common\" in command-list.txt\n> > classification). Since the people who type \"git help\" are mostly new\n> > people, I think promoting the friendlier alternative is a good idea.\n>\n> So it is mainly how the word \"common\" is defined?  It's not how\n> common the command is in the minds of the population of Git users,\n> but how common we want it to be?\n\nIn this context, I think so. I suppose \"recommended\" is a better way\nto describe it since we suggest a common set of commands to the user.\n-- \nDuy\n"},{"id":"373635","messageId":"20190411131218.19195-1-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190308101655.9767-1-pclouds@gmail.com","subject":"[PATCH v2 00/16] Add new command 'restore'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:02Z","receivedAt":"2019-04-11T13:12:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the companion of \"git switch\" and is based on that topic.\nThis command peforms the \"checkout paths\" from git-checkout, git-reset\nand also has a third mode to reset only worktree, leaving the index\nalone.\n\nFor new people not aware of previous discussions, this command is\nsupposed to be a friendlier replacement for \"git checkout\" and\nhopefully fixes some warts that have grown over the time in\ngit-checkout. For this reason, the last patch starts to recommend \"git\nrestore\" everywhere.\n\nv2 should address all the comments from v1. I still haven't done that\n--intent-to-add thing yet even though I'd like to make it default\nbehavior too. Anyway changes are\n\n- --index is renamed to --staged to avoid conflict with --index from\n  git-apply, which has different meaning.\n\n- lots of document fixes. The comparison between restore/revert/reset\n  is added in git.txt instead. I want to avoid duplicate the same\n  thing on three man pages.\n\n- --force is dropped. --ignore-unmerged takes its place.\n\n- --recurse-submodules is dropped. I've concluded that restoring files\n  across submodule boundary is not exactly supported yet. Let's leave it\n  for later.\n\n- git-rm learns about --staged as an alias of --cached (in fact it's\n  more the other way around). This is to keep suggestions consistent\n  because we tell people to do \"git foo --staged\" everywhere.\n\n- git-reset and git-diff are moved to other groups in \"git help\"\n  output.\n\nNguyễn Thái Ngọc Duy (16):\n  checkout: split part of it to new command 'restore'\n  restore: take tree-ish from --source option instead\n  restore: make pathspec mandatory\n  restore: disable overlay mode by default\n  checkout: factor out worktree checkout code\n  restore: add --worktree and --staged\n  restore: reject invalid combinations with --staged\n  restore: default to --source=HEAD when only --staged is specified\n  restore: replace --force with --ignore-unmerged\n  restore: support --patch\n  t: add tests for restore\n  completion: support restore\n  user-manual.txt: prefer 'merge --abort' over 'reset --hard'\n  doc: promote \"git restore\"\n  rm: add --staged as alias for --cached\n  help: move git-diff and git-reset to different groups\n\n .gitignore                             |   1 +\n Documentation/config/interactive.txt   |   3 +-\n Documentation/git-checkout.txt         |   3 +-\n Documentation/git-clean.txt            |   2 +-\n Documentation/git-commit.txt           |   2 +-\n Documentation/git-format-patch.txt     |   2 +-\n Documentation/git-reset.txt            |  13 +-\n Documentation/git-restore.txt (new)    | 183 +++++++++++++++\n Documentation/git-revert.txt           |   7 +-\n Documentation/git-rm.txt               |   7 +-\n Documentation/git.txt                  |  20 ++\n Documentation/gitcli.txt               |   4 +-\n Documentation/giteveryday.txt          |   5 +-\n Documentation/gittutorial-2.txt        |   4 +-\n Documentation/gittutorial.txt          |   2 +-\n Documentation/user-manual.txt          |  14 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/checkout.c                     | 299 +++++++++++++++++++------\n builtin/clone.c                        |   2 +-\n builtin/commit.c                       |   2 +-\n builtin/rm.c                           |   7 +-\n command-list.txt                       |   7 +-\n contrib/completion/git-completion.bash |  15 ++\n git-add--interactive.perl              |  52 +++++\n git.c                                  |   1 +\n t/lib-patch-mode.sh                    |  12 +\n t/t2070-restore.sh (new +x)            |  99 ++++++++\n t/t2071-restore-patch.sh (new +x)      | 110 +++++++++\n t/t3600-rm.sh                          |   6 +-\n t/t7508-status.sh                      |  84 +++----\n t/t7512-status-help.sh                 |  20 +-\n wt-status.c                            |  30 ++-\n 33 files changed, 848 insertions(+), 172 deletions(-)\n create mode 100644 Documentation/git-restore.txt\n create mode 100755 t/t2070-restore.sh\n create mode 100755 t/t2071-restore-patch.sh\n\nDiff:\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ee0d164d0e..a294652dd6 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -570,7 +570,7 @@ $ git add frotz\n \n SEE ALSO\n --------\n-linkgit:git-switch[1]\n+linkgit:git-switch[1],\n linkgit:git-restore[1]\n \n GIT\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex e7ac8eb9e8..7628193284 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -56,7 +56,7 @@ summary of what is included by any of the above for the next\n commit by giving the same set of parameters (options and paths).\n \n If you make a commit and then find a mistake immediately after\n-that, you can recover from it with 'git restore' or 'git reset'.\n+that, you can recover from it with 'git reset'.\n \n \n OPTIONS\n@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in\n your working tree are temporarily stored to a staging area\n called the \"index\" with 'git add'.  A file can be\n reverted back, only in the index but not in the working tree,\n-to that of the last commit with `git restore --index <file>`,\n+to that of the last commit with `git restore --staged <file>`,\n which effectively reverts 'git add' and prevents the changes to\n this file from participating in the next commit.  After building\n the state to be committed incrementally with these commands,\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 71c9fe3af3..01bfcecf69 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:\n \n     $ git fetch <project> master:test-apply\n     $ git switch test-apply\n-    $ git restore --source=HEAD --index --worktree :/\n+    $ git restore --source=HEAD --staged --worktree :/\n     $ git am a.patch\n \n If it does not apply correctly, there can be various reasons.\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex b70281677f..633d71d36a 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.\n \tthe current branch.)\n +\n This means that `git reset <paths>` is the opposite of `git add\n-<paths>`.\n+<paths>`. This command is equivalent to\n+`git restore [--source=<tree-ish>] --staged <paths>...`.\n +\n After running `git reset <paths>` to update the index entry, you can\n use linkgit:git-restore[1] to check the contents out of the index to\n@@ -86,8 +87,8 @@ but carries forward unmerged index entries.\n \tchanges, reset is aborted.\n --\n \n-If you want to undo a commit other than the latest on a branch,\n-linkgit:git-revert[1] is your friend.\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n \n \n OPTIONS\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex a667a5ced4..b608f3f360 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -8,48 +8,60 @@ git-restore - Restore working tree files\n SYNOPSIS\n --------\n [verse]\n-'git restore' [<options>] [--source=<revision>] <pathspec>...\n-'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]\n+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...\n+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]\n \n DESCRIPTION\n -----------\n-Restore paths in the working tree by replacing with the contents in\n-the restore source or remove if the paths do not exist in the restore\n-source. The source is by default the index but could be from a commit.\n-The command can also optionally restore content in the index from a\n-commit.\n+Restore specified paths in the working tree with some contents from a\n+restore source. If a path is tracked but does not exist in the restore\n+source, it will be removed to match the source.\n \n-When a `<revision>` is given, the paths that match the `<pathspec>` are\n-updated both in the index and in the working tree.\n+The command can also be used to restore the content in the index with\n+`--staged`, or restore both the working tree and the index with\n+`--staged --worktree`.\n+\n+By default, the restore sources for working tree and the index are the\n+index and `HEAD` respectively. `--source` could be used to specify a\n+commit as the restore source.\n+\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n \n OPTIONS\n -------\n--s<tree>::\n+-s <tree>::\n --source=<tree>::\n \tRestore the working tree files with the content from the given\n-\ttree or any revision that leads to a tree (e.g. a commit or a\n-\tbranch).\n+\ttree. It is common to specify the source tree by naming a\n+\tcommit, branch or tag associated with it.\n++\n+If not specified, the default restore source for the working tree is\n+the index, and the default restore source for the index index is\n+`HEAD`. When both `--staged` and `--worktree` are specified,\n+`--source` must also be specified.\n \n -p::\n --patch::\n \tInteractively select hunks in the difference between the\n-\t`<revision>` (or the index, if unspecified) and the working\n-\ttree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n-\tto learn how to operate the `--patch` mode.\n+\trestore source and the restore location. See the ``Interactive\n+\tMode'' section of linkgit:git-add[1] to learn how to operate\n+\tthe `--patch` mode.\n++\n+Note that `--patch` can accept no pathspec and will prompt to restore\n+all modified paths.\n \n -W::\n --worktree::\n--I::\n---index::\n+-S::\n+--staged::\n \tSpecify the restore location. If neither option is specified,\n-\tby default the working tree is restored. If `--index` is\n-\tspecified without `--worktree` or `--source`, `--source=HEAD`\n-\tis implied. These options only make sense to use with\n-\t`--source`.\n+\tby default the working tree is restored. Specifying `--staged`\n+\twill only restore the index. Specifying both restores both.\n \n -q::\n --quiet::\n-\tQuiet, suppress feedback messages.\n+\tQuiet, suppress feedback messages. Implies `--no-progress`.\n \n --progress::\n --no-progress::\n@@ -58,16 +70,10 @@ OPTIONS\n \tis specified. This flag enables progress reporting even if not\n \tattached to a terminal, regardless of `--quiet`.\n \n--f::\n---force::\n-\tIf `--source` is not specified, unmerged entries are left alone\n-\tand will not fail the operation. Unmerged entries are always\n-\treplaced if `--source` is specified, regardless of `--force`.\n-\n --ours::\n --theirs::\n-\tCheck out stage #2 ('ours') or #3 ('theirs') for unmerged\n-\tpaths.\n+\tWhen restoring files in the working tree from the index, use\n+\tstage #2 ('ours') or #3 ('theirs') for unmerged paths.\n +\n Note that during `git rebase` and `git pull --rebase`, 'ours' and\n 'theirs' may appear swapped. See the explanation of the same options\n@@ -75,67 +81,64 @@ in linkgit:git-checkout[1] for details.\n \n -m::\n --merge::\n-\tRecreate the conflicted merge in the specified paths.\n+\tWhen restoring files on the working tree from the index,\n+\trecreate the conflicted merge in the unmerged paths.\n \n --conflict=<style>::\n \tThe same as `--merge` option above, but changes the way the\n-\tconflicting hunks are presented, overriding the merge.conflictStyle\n-\tconfiguration variable.  Possible values are \"merge\" (default)\n-\tand \"diff3\" (in addition to what is shown by \"merge\" style,\n-\tshows the original contents).\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values\n+\tare \"merge\" (default) and \"diff3\" (in addition to what is\n+\tshown by \"merge\" style, shows the original contents).\n+\n+--ignore-unmerged::\n+\tWhen restoring files on the working tree from the index, do\n+\tnot abort the operation if there are unmerged entries and\n+\tneither `--ours`, `--theirs`, `--merge` or `--conflict` is\n+\tspecified. Unmerged paths on the working tree are left alone.\n \n --ignore-skip-worktree-bits::\n-\tIn sparse checkout mode, by default update only entries\n+\tIn sparse checkout mode, by default is to only update entries\n \tmatched by `<pathspec>` and sparse patterns in\n \t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n-\tpatterns and unconditionally restores any files in `<pathspec>`.\n-\n---recurse-submodules::\n---no-recurse-submodules::\n-\tUsing `--recurse-submodules` will update the content of all initialized\n-\tsubmodules according to the commit recorded in the superproject. If\n-\tlocal modifications in a submodule would be overwritten the checkout\n-\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n-\tis used, the work trees of submodules will not be updated.\n-\tJust like linkgit:git-submodule[1], this will detach the\n-\tsubmodules HEAD.\n+\tpatterns and unconditionally restores any files in\n+\t`<pathspec>`.\n \n --overlay::\n --no-overlay::\n-\tIn overlay mode, `git checkout` never removes files from the\n-\tindex or the working tree. In no-overlay mode, files that\n-\tappear in the index and working tree, but not in `--source` tree\n-\tare removed, to make them match `<tree-ish>` exactly. The\n-\tdefault is no-overlay mode.\n+\tIn overlay mode, the command never removes files when\n+\trestoring. In no-overlay mode, tracked files that do not\n+\tappear in the `--source` tree are removed, to make them match\n+\t`<tree>` exactly. The default is no-overlay mode.\n \n EXAMPLES\n --------\n \n-The following sequence checks out the `master` branch, reverts\n-the `Makefile` to two revisions back, deletes hello.c by\n-mistake, and gets it back from the index.\n+The following sequence switches to the `master` branch, reverts the\n+`Makefile` to two revisions back, deletes hello.c by mistake, and gets\n+it back from the index.\n \n ------------\n $ git switch master\n $ git restore --source master~2 Makefile  <1>\n $ rm -f hello.c\n-$ git restore hello.c                   <2>\n+$ git restore hello.c                     <2>\n ------------\n \n <1> take a file out of another commit\n <2> restore hello.c from the index\n \n-If you want to check out _all_ C source files out of the index,\n-you can say\n+If you want to restore _all_ C source files to match the version in\n+the index, you can say\n \n ------------\n $ git restore '*.c'\n ------------\n \n Note the quotes around `*.c`.  The file `hello.c` will also be\n-checked out, even though it is no longer in the working tree,\n-because the file globbing is used to match entries in the index\n-(not in the working tree by the shell).\n+restored, even though it is no longer in the working tree, because the\n+file globbing is used to match entries in the index (not in the\n+working tree by the shell).\n \n To restore all files in the current directory\n \n@@ -144,28 +147,36 @@ $ git restore .\n ------------\n \n or to restore all working tree files with 'top' pathspec magic (see\n-linkgit::gitglossary[7])\n+linkgit:gitglossary[7])\n \n ------------\n $ git restore :/\n ------------\n \n-To restore a file in the index only (this is the same as using\n-\"git reset\")\n+To restore a file in the index to match the version in `HEAD` (this is\n+the same as using linkgit:git-reset[1])\n+\n+------------\n+$ git restore --staged hello.c\n+------------\n+\n+or you can restore both the index and the working tree (this the same\n+as using linkgit:git-checkout[1])\n \n ------------\n-$ git restore --index hello.c\n+$ git restore --source=HEAD --staged --worktree hello.c\n ------------\n \n-or you can restore both the index and the working tree\n+or the short form which is more practical but less readable:\n \n ------------\n-$ git restore --source=HEAD --index --worktree hello.c\n+$ git restore -s@ -SW hello.c\n ------------\n \n SEE ALSO\n --------\n-linkgit:git-checkout[1]\n+linkgit:git-checkout[1],\n+linkgit:git-reset[1]\n \n GIT\n ---\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex c38bc54439..9aadc36881 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -27,9 +27,12 @@ throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n should see linkgit:git-restore[1], specifically the `--source`\n-option  Take care with these alternatives as\n+option. Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n+\n OPTIONS\n -------\n <commit>...::\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex b5c46223c4..4271fc5eaa 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,7 +8,7 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f | --force] [-n] [-r] [--staged] [--ignore-unmatch] [--quiet] [--] <file>...\n \n DESCRIPTION\n -----------\n@@ -55,10 +55,11 @@ OPTIONS\n \tthe list of files, (useful when filenames might be mistaken\n \tfor command-line options).\n \n+--staged::\n --cached::\n \tUse this option to unstage and remove paths only from the index.\n-\tWorking tree files, whether modified or not, will be\n-\tleft alone.\n+\tWorking tree files, whether modified or not, will be left\n+\talone. `--cached` is synonym for `--staged`.\n \n --ignore-unmatch::\n \tExit with a zero status even if no files matched.\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 00156d64aa..fbed007354 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -210,6 +210,26 @@ people via patch over e-mail.\n \n include::cmds-foreignscminterface.txt[]\n \n+Reset, restore and revert\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+There are three commands with similar names: `git reset`,\n+`git restore` and `git revert`.\n+\n+* linkgit:git-revert[1] is about making a new commit that reverts the\n+  changes made by other commits.\n+\n+* linkgit:git-restore[1] is about restoring files in the working tree\n+  from either the index or another commit. This command does not\n+  update your branch. The command can also be used to restore files in\n+  the index from another commit.\n+\n+* linkgit:git-reset[1] is about updating your branch, moving the tip\n+  in order to add or remove commits from the branch. This operation\n+  changes the commit history.\n++\n+`git reset` can also be used to restore the index, overlapping with\n+`git restore`.\n+\n \n Low-level commands (plumbing)\n -----------------------------\ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 79517b22f9..1bd919f92b 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -51,8 +51,7 @@ following commands.\n \n   * linkgit:git-commit[1] to advance the current branch.\n \n-  * linkgit:git-reset[1] and linkgit:git-restore[1] (with\n-    pathname parameters) to undo changes.\n+  * linkgit:git-restore[1] to undo changes.\n \n   * linkgit:git-merge[1] to merge between local branches.\n \ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e412841488..8bdb7d0bd3 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -370,7 +370,7 @@ situation:\n $ git status\n On branch master\n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   closing.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 6266ca21b4..59ef5cef1f 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -110,7 +110,7 @@ $ git status\n On branch master\n Changes to be committed:\n Your branch is up to date with 'origin/master'.\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   file1\n \tmodified:   file2\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 02713886f0..8bce75b2cf 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed\n state with\n \n -------------------------------------------------\n-$ git restore --index --worktree :/\n+$ git restore --staged --worktree :/\n -------------------------------------------------\n \n If you make a commit that you later wish you hadn't, there are two\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e8896ddbca..bed79ae595 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {\n \tNULL,\n };\n \n-static const char * const restore_files_usage[] = {\n+static const char * const restore_usage[] = {\n \tN_(\"git restore [<options>] [--source=<branch>] <file>...\"),\n \tNULL,\n };\n@@ -68,6 +68,8 @@ struct checkout_opts {\n \tint empty_pathspec_ok;\n \tint checkout_index;\n \tint checkout_worktree;\n+\tconst char *ignore_unmerged_opt;\n+\tint ignore_unmerged;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->new_branch_log)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-l\");\n \n-\tif (opts->force && opts->patch_mode)\n-\t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-f\");\n+\tif (opts->ignore_unmerged && opts->patch_mode)\n+\t\tdie(_(\"'%s' cannot be used with updating paths\"),\n+\t\t    opts->ignore_unmerged_opt);\n \n \tif (opts->force_detach)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--detach\");\n@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->merge && opts->patch_mode)\n \t\tdie(_(\"'%s' cannot be used with %s\"), \"--merge\", \"--patch\");\n \n-\tif (opts->force && opts->merge)\n-\t\tdie(_(\"'%s' cannot be used with %s\"), \"-f\", \"-m\");\n+\tif (opts->ignore_unmerged && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with %s\"),\n+\t\t    opts->ignore_unmerged_opt, \"-m\");\n \n \tif (opts->new_branch)\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n@@ -427,12 +431,22 @@ static int checkout_paths(const struct checkout_opts *opts,\n \n \tif (!opts->checkout_worktree && !opts->checkout_index)\n \t\tdie(_(\"neither '%s' or '%s' is specified\"),\n-\t\t    \"--index\", \"--worktree\");\n+\t\t    \"--staged\", \"--worktree\");\n \n-\tif (!opts->source_tree && !opts->checkout_worktree)\n+\tif (!opts->checkout_worktree && !opts->from_treeish)\n \t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n \t\t    \"--worktree\", \"--source\");\n \n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->writeout_stage)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--ours\", \"--theirs\", \"--staged\");\n+\n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->merge)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--merge\", \"--conflict\", \"--staged\");\n+\n \tif (opts->patch_mode) {\n \t\tconst char *patch_mode;\n \n@@ -443,7 +457,8 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\telse if (!opts->checkout_index && opts->checkout_worktree)\n \t\t\tpatch_mode = \"--patch=worktree\";\n \t\telse\n-\t\t\tBUG(\"either flag must have been set\");\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(revision, patch_mode, &opts->pathspec);\n \t}\n \n@@ -486,8 +501,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tif (ce->ce_flags & CE_MATCHED) {\n \t\t\tif (!ce_stage(ce))\n \t\t\t\tcontinue;\n-\t\t\tif (opts->force) {\n-\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n+\t\t\tif (opts->ignore_unmerged) {\n+\t\t\t\tif (!opts->quiet)\n+\t\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n \t\t\t} else if (opts->writeout_stage) {\n \t\t\t\terrs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);\n \t\t\t} else if (opts->merge) {\n@@ -1405,8 +1421,6 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n@@ -1424,6 +1438,8 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n@@ -1493,8 +1509,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n-\tif (opts->force)\n+\tif (opts->force) {\n \t\topts->discard_changes = 1;\n+\t\topts->ignore_unmerged_opt = \"--force\";\n+\t\topts->ignore_unmerged = 1;\n+\t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1516,8 +1535,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n \t\tBUG(\"these flags should be non-negative by now\");\n \t/*\n-\t * convenient shortcut: \"git restore --index\" equals\n-\t * \"git restore --index --source HEAD\"\n+\t * convenient shortcut: \"git restore --staged\" equals\n+\t * \"git restore --staged --source HEAD\"\n \t */\n \tif (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)\n \t\topts->from_treeish = \"HEAD\";\n@@ -1587,7 +1606,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \n \tif (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&\n \t    !opts->patch_mode)\t/* patch mode is special */\n-\t\tdie(_(\"pathspec is required\"));\n+\t\tdie(_(\"you must specify path(s) to restore\"));\n \n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n@@ -1737,10 +1756,12 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n-\t\tOPT_BOOL('I', \"index\", &opts.checkout_index,\n+\t\tOPT_BOOL('S', \"staged\", &opts.checkout_index,\n \t\t\t   N_(\"restore the index\")),\n \t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n \t\t\t   N_(\"restore the working tree (default)\")),\n+\t\tOPT_BOOL(0, \"ignore-unmerged\", &opts.ignore_unmerged,\n+\t\t\t N_(\"ignore unmerged entries\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1753,13 +1774,14 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.overlay_mode = 0;\n \topts.checkout_index = -1;    /* default off */\n \topts.checkout_worktree = -2; /* default on */\n+\topts.ignore_unmerged_opt = \"--ignore-unmerged\";\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n-\t\t\t    options, restore_files_usage);\n+\t\t\t    options, restore_usage);\n \tFREE_AND_NULL(options);\n \treturn ret;\n }\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c530264e63..fa5982cc86 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n-\t\t      \"not exceeded, and then \\\"git restore --index :/\\\" to recover.\"));\n+\t\t      \"not exceeded, and then \\\"git restore --staged :/\\\" to recover.\"));\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))\n \t\twrite_commit_graph_reachable(get_object_directory(), 0, 0);\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex db85b33982..47c8eb100b 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -217,7 +217,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t\t     \"staged in the index:\",\n \t\t\t     \"the following files have changes \"\n \t\t\t     \"staged in the index:\", files_cached.nr),\n-\t\t\t  _(\"\\n(use --cached to keep the file,\"\n+\t\t\t  _(\"\\n(use --staged to keep the file,\"\n \t\t\t    \" or -f to force removal)\"),\n \t\t\t  &errs);\n \tstring_list_clear(&files_cached, 0);\n@@ -226,7 +226,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t\t  Q_(\"the following file has local modifications:\",\n \t\t\t     \"the following files have local modifications:\",\n \t\t\t     files_local.nr),\n-\t\t\t  _(\"\\n(use --cached to keep the file,\"\n+\t\t\t  _(\"\\n(use --staged to keep the file,\"\n \t\t\t    \" or -f to force removal)\"),\n \t\t\t  &errs);\n \tstring_list_clear(&files_local, 0);\n@@ -240,7 +240,8 @@ static int ignore_unmatch = 0;\n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n \tOPT__QUIET(&quiet, N_(\"do not list removed files\")),\n-\tOPT_BOOL( 0 , \"cached\",         &index_only, N_(\"only remove from the index\")),\n+\tOPT_BOOL( 0 , \"staged\",         &index_only, N_(\"only remove from the index\")),\n+\tOPT_BOOL( 0 , \"cached\",         &index_only, N_(\"synonym for --staged\")),\n \tOPT__FORCE(&force, N_(\"override the up-to-date check\"), PARSE_OPT_NOCOMPLETE),\n \tOPT_BOOL('r', NULL,             &recursive,  N_(\"allow recursive removal\")),\n \tOPT_BOOL( 0 , \"ignore-unmatch\", &ignore_unmatch,\ndiff --git a/command-list.txt b/command-list.txt\nindex cf8dccb439..a9ac72bef4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface\n git-cvsserver                           foreignscminterface\n git-daemon                              synchingrepositories\n git-describe                            mainporcelain\n-git-diff                                mainporcelain           history\n+git-diff                                mainporcelain           info\n git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff-tree                           plumbinginterrogators\n@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n-git-reset                               mainporcelain           worktree\n+git-reset                               mainporcelain           history\n git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nindex df91bf54bc..73ea13ede9 100755\n--- a/t/t2070-restore.sh\n+++ b/t/t2070-restore.sh\n@@ -23,11 +23,6 @@ test_expect_success 'restore without pathspec is not ok' '\n \ttest_must_fail git restore --source=first\n '\n \n-test_expect_success 'restore -p without pathspec is fine' '\n-\techo q >cmd &&\n-\tgit restore -p <cmd\n-'\n-\n test_expect_success 'restore a file, ignoring branch of same name' '\n \tcat one >expected &&\n \techo dirty >>one &&\n@@ -35,7 +30,7 @@ test_expect_success 'restore a file, ignoring branch of same name' '\n \ttest_cmp expected one\n '\n \n-test_expect_success 'restore a file on worktree from another branch' '\n+test_expect_success 'restore a file on worktree from another ref' '\n \ttest_when_finished git reset --hard &&\n \tgit cat-file blob first:./first.t >expected &&\n \tgit restore --source=first first.t &&\n@@ -45,33 +40,60 @@ test_expect_success 'restore a file on worktree from another branch' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'restore a file in the index from another branch' '\n+test_expect_success 'restore a file in the index from another ref' '\n \ttest_when_finished git reset --hard &&\n \tgit cat-file blob first:./first.t >expected &&\n-\tgit restore --source=first --index first.t &&\n+\tgit restore --source=first --staged first.t &&\n \tgit show :first.t >actual &&\n \ttest_cmp expected actual &&\n \tgit cat-file blob HEAD:./first.t >expected &&\n \ttest_cmp expected first.t\n '\n \n-test_expect_success 'restore a file in both the index and worktree from another branch' '\n+test_expect_success 'restore a file in both the index and worktree from another ref' '\n \ttest_when_finished git reset --hard &&\n \tgit cat-file blob first:./first.t >expected &&\n-\tgit restore --source=first --index --worktree first.t &&\n+\tgit restore --source=first --staged --worktree first.t &&\n \tgit show :first.t >actual &&\n \ttest_cmp expected actual &&\n \ttest_cmp expected first.t\n '\n \n-test_expect_success 'restore --index uses HEAD as source' '\n+test_expect_success 'restore --staged uses HEAD as source' '\n \ttest_when_finished git reset --hard &&\n \tgit cat-file blob :./first.t >expected &&\n \techo index-dirty >>first.t &&\n \tgit add first.t &&\n-\tgit restore --index first.t &&\n+\tgit restore --staged first.t &&\n \tgit cat-file blob :./first.t >actual &&\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '\n+\tgit init unmerged &&\n+\t(\n+\t\tcd unmerged &&\n+\t\techo one >unmerged &&\n+\t\techo one >common &&\n+\t\tgit add unmerged common &&\n+\t\tgit commit -m common &&\n+\t\tgit switch -c first &&\n+\t\techo first >unmerged &&\n+\t\tgit commit -am first &&\n+\t\tgit switch -c second master &&\n+\t\techo second >unmerged &&\n+\t\tgit commit -am second &&\n+\t\ttest_must_fail git merge first &&\n+\n+\t\techo dirty >>common &&\n+\t\ttest_must_fail git restore . &&\n+\n+\t\tgit restore --ignore-unmerged --quiet . >output 2>&1 &&\n+\t\tgit diff common >diff-output &&\n+\t\t: >empty &&\n+\t\ttest_cmp empty output &&\n+\t\ttest_cmp empty diff-output\n+\t)\n+'\n+\n test_done\ndiff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\nindex 46ebcb2413..98b2476e7c 100755\n--- a/t/t2071-restore-patch.sh\n+++ b/t/t2071-restore-patch.sh\n@@ -16,6 +16,11 @@ test_expect_success PERL 'setup' '\n \tsave_head\n '\n \n+test_expect_success PERL 'restore -p without pathspec is fine' '\n+\techo q >cmd &&\n+\tgit restore -p <cmd\n+'\n+\n # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'\n \n test_expect_success PERL 'saying \"n\" does nothing' '\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 04e5d42bd3..5686032b8c 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -797,7 +797,7 @@ test_expect_success 'rm file with local modification' '\n \tcat >expect <<-\\EOF &&\n \terror: the following file has local modifications:\n \t    foo.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \tgit commit -m \"testing rm 3\" &&\n \techo content3 >foo.txt &&\n@@ -819,7 +819,7 @@ test_expect_success 'rm file with changes in the index' '\n \tcat >expect <<-\\EOF &&\n \terror: the following file has changes staged in the index:\n \t    foo.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \tgit reset --hard &&\n \techo content5 >foo.txt &&\n@@ -845,7 +845,7 @@ test_expect_success 'rm files with two different errors' '\n \t(use -f to force removal)\n \terror: the following file has changes staged in the index:\n \t    bar1.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \techo content >foo1.txt &&\n \tgit add foo1.txt &&\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex d5cf0185b7..738f3df2f9 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -71,7 +71,7 @@ test_expect_success 'setup' '\n '\n \n test_expect_success 'status (1)' '\n-\ttest_i18ngrep \"use \\\"git rm --cached <file>\\.\\.\\.\\\" to unstage\" output\n+\ttest_i18ngrep \"use \\\"git rm --staged <file>\\.\\.\\.\\\" to unstage\" output\n '\n \n strip_comments () {\n@@ -94,7 +94,7 @@ test_expect_success 'status --column' '\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git restore --index <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n@@ -128,7 +128,7 @@ cat >expect <<\\EOF\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git restore --index <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n@@ -278,7 +278,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -347,7 +347,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -420,7 +420,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -484,7 +484,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -542,7 +542,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -605,7 +605,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   ../dir2/added\n \n@@ -676,7 +676,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \t<GREEN>new file:   dir2/added<RESET>\n \n@@ -802,7 +802,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   dir1/modified\n \n@@ -896,7 +896,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n@@ -956,7 +956,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n@@ -1068,7 +1068,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --source=HEAD^1 --index <file>...\" to unstage)\n+  (use \"git restore --source=HEAD^1 --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n@@ -1123,7 +1123,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n@@ -1235,7 +1235,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n@@ -1295,7 +1295,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n@@ -1379,7 +1379,7 @@ cat > expect << EOF\n ;   (use \"git pull\" to merge the remote branch into yours)\n ;\n ; Changes to be committed:\n-;   (use \"git restore --index <file>...\" to unstage)\n+;   (use \"git restore --staged <file>...\" to unstage)\n ;\n ;\tmodified:   sm\n ;\n@@ -1458,7 +1458,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n@@ -1581,7 +1581,7 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 6e678456bc..1b9712c675 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -85,7 +85,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -110,7 +110,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -148,7 +148,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -176,7 +176,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Unmerged paths:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   to-revert.txt\n@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Changes to be committed:\n-  (use \"git restore --index <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   to-revert.txt\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 3098e77d72..4d065ce89e 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -181,13 +181,13 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n \telse if (!s->is_initial) {\n \t\tif (!strcmp(s->reference, \"HEAD\"))\n \t\t\tstatus_printf_ln(s, c,\n-\t\t\t\t\t _(\"  (use \\\"git restore --index <file>...\\\" to unstage)\"));\n+\t\t\t\t\t _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n \t\telse\n \t\t\tstatus_printf_ln(s, c,\n-\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --index <file>...\\\" to unstage)\"),\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n \t\t\t\t\t s->reference);\n \t} else\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n+\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --staged <file>...\\\" to unstage)\"));\n \n \tif (!both_deleted) {\n \t\tif (!del_mod_conflict)\n@@ -214,13 +214,13 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)\n \telse if (!s->is_initial) {\n \t\tif (!strcmp(s->reference, \"HEAD\"))\n \t\t\tstatus_printf_ln(s, c\n-\t\t\t\t\t , _(\"  (use \\\"git restore --index <file>...\\\" to unstage)\"));\n+\t\t\t\t\t , _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n \t\telse\n \t\t\tstatus_printf_ln(s, c,\n-\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --index <file>...\\\" to unstage)\"),\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n \t\t\t\t\t s->reference);\n \t} else\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n+\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --staged <file>...\\\" to unstage)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n\n\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373636","messageId":"20190411131218.19195-2-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 01/16] checkout: split part of it to new command 'restore'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:03Z","receivedAt":"2019-04-11T13:12:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Previously the switching branch business of 'git checkout' becomes a\nnew command 'switch'. This adds the restore command for the checking\nout paths path.\n\nSimilar to git-switch, a new man page is added to describe what the\ncommand will become. The implementation will be updated shortly to\nmatch the man page.\n\nA couple main differences from 'git checkout <paths>':\n\n- 'restore' by default will only update worktree. This matters more\n  when --source is specified ('checkout <tree> <paths>' updates both\n  worktree and index).\n\n- 'restore --staged' can be used to restore the index. This command\n  overlaps with 'git reset <paths>'.\n\n- both worktree and index could also be restored at the same time\n  (from a tree) when both --staged and --worktree are specified. This\n  overlaps with 'git checkout <tree> <paths>'\n\n- default source for restoring worktree and index is the index and\n  HEAD respectively. A different (tree) source could be specified as\n  with --source (*).\n\n- when both index and worktree are restored, --source must be\n  specified since the default source for these two individual targets\n  are different (**)\n\n- --no-overlay is enabled by default, if an entry is missing in the\n  source, restoring means deleting the entry\n\n(*) I originally went with --from instead of --source. I still think\n  --from is a better name. The short option -f however is already\n  taken by force. And I do think short option is good to have, e.g. to\n  write -s@ or -s@^ instead of --source=HEAD.\n\n(**) If you sit down and think about it, moving worktree's source from\n  the index to HEAD makes sense, but nobody is really thinking it\n  through when they type the commands.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                           |   1 +\n Documentation/config/interactive.txt |   3 +-\n Documentation/git-checkout.txt       |   3 +-\n Documentation/git-reset.txt          |   7 +-\n Documentation/git-restore.txt (new)  | 183 +++++++++++++++++++++++++++\n Documentation/git-revert.txt         |   3 +\n Documentation/git.txt                |  20 +++\n Makefile                             |   1 +\n builtin.h                            |   1 +\n builtin/checkout.c                   |  26 ++++\n command-list.txt                     |   1 +\n git.c                                |   1 +\n 12 files changed, 245 insertions(+), 5 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex c687b92b1c..fb377106be 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -143,6 +143,7 @@\n /git-request-pull\n /git-rerere\n /git-reset\n+/git-restore\n /git-rev-list\n /git-rev-parse\n /git-revert\ndiff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt\nindex ad846dd7c9..a2d3c7ec44 100644\n--- a/Documentation/config/interactive.txt\n+++ b/Documentation/config/interactive.txt\n@@ -2,7 +2,8 @@ interactive.singleKey::\n \tIn interactive commands, allow the user to provide one-letter\n \tinput with a single key (i.e., without hitting enter).\n \tCurrently this is used by the `--patch` mode of\n-\tlinkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],\n+\tlinkgit:git-add[1], linkgit:git-checkout[1],\n+\tlinkgit:git-restore[1], linkgit:git-commit[1],\n \tlinkgit:git-reset[1], and linkgit:git-stash[1]. Note that this\n \tsetting is silently ignored if portable keystroke input\n \tis not available; requires the Perl module Term::ReadKey.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 58f18a0842..a294652dd6 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -570,7 +570,8 @@ $ git add frotz\n \n SEE ALSO\n --------\n-linkgit:git-switch[1]\n+linkgit:git-switch[1],\n+linkgit:git-restore[1]\n \n GIT\n ---\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex cbf901efb4..c25f8a95b9 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.\n \tthe current branch.)\n +\n This means that `git reset <paths>` is the opposite of `git add\n-<paths>`.\n+<paths>`. This command is equivalent to\n+`git restore [--source=<tree-ish>] --staged <paths>...`.\n +\n After running `git reset <paths>` to update the index entry, you can\n use linkgit:git-checkout[1] to check the contents out of the index to\n@@ -86,8 +87,8 @@ but carries forward unmerged index entries.\n \tchanges, reset is aborted.\n --\n \n-If you want to undo a commit other than the latest on a branch,\n-linkgit:git-revert[1] is your friend.\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n \n \n OPTIONS\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nnew file mode 100644\nindex 0000000000..b608f3f360\n--- /dev/null\n+++ b/Documentation/git-restore.txt\n@@ -0,0 +1,183 @@\n+git-restore(1)\n+==============\n+\n+NAME\n+----\n+git-restore - Restore working tree files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...\n+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+Restore specified paths in the working tree with some contents from a\n+restore source. If a path is tracked but does not exist in the restore\n+source, it will be removed to match the source.\n+\n+The command can also be used to restore the content in the index with\n+`--staged`, or restore both the working tree and the index with\n+`--staged --worktree`.\n+\n+By default, the restore sources for working tree and the index are the\n+index and `HEAD` respectively. `--source` could be used to specify a\n+commit as the restore source.\n+\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n+\n+OPTIONS\n+-------\n+-s <tree>::\n+--source=<tree>::\n+\tRestore the working tree files with the content from the given\n+\ttree. It is common to specify the source tree by naming a\n+\tcommit, branch or tag associated with it.\n++\n+If not specified, the default restore source for the working tree is\n+the index, and the default restore source for the index index is\n+`HEAD`. When both `--staged` and `--worktree` are specified,\n+`--source` must also be specified.\n+\n+-p::\n+--patch::\n+\tInteractively select hunks in the difference between the\n+\trestore source and the restore location. See the ``Interactive\n+\tMode'' section of linkgit:git-add[1] to learn how to operate\n+\tthe `--patch` mode.\n++\n+Note that `--patch` can accept no pathspec and will prompt to restore\n+all modified paths.\n+\n+-W::\n+--worktree::\n+-S::\n+--staged::\n+\tSpecify the restore location. If neither option is specified,\n+\tby default the working tree is restored. Specifying `--staged`\n+\twill only restore the index. Specifying both restores both.\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages. Implies `--no-progress`.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+--ours::\n+--theirs::\n+\tWhen restoring files in the working tree from the index, use\n+\tstage #2 ('ours') or #3 ('theirs') for unmerged paths.\n++\n+Note that during `git rebase` and `git pull --rebase`, 'ours' and\n+'theirs' may appear swapped. See the explanation of the same options\n+in linkgit:git-checkout[1] for details.\n+\n+-m::\n+--merge::\n+\tWhen restoring files on the working tree from the index,\n+\trecreate the conflicted merge in the unmerged paths.\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values\n+\tare \"merge\" (default) and \"diff3\" (in addition to what is\n+\tshown by \"merge\" style, shows the original contents).\n+\n+--ignore-unmerged::\n+\tWhen restoring files on the working tree from the index, do\n+\tnot abort the operation if there are unmerged entries and\n+\tneither `--ours`, `--theirs`, `--merge` or `--conflict` is\n+\tspecified. Unmerged paths on the working tree are left alone.\n+\n+--ignore-skip-worktree-bits::\n+\tIn sparse checkout mode, by default is to only update entries\n+\tmatched by `<pathspec>` and sparse patterns in\n+\t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n+\tpatterns and unconditionally restores any files in\n+\t`<pathspec>`.\n+\n+--overlay::\n+--no-overlay::\n+\tIn overlay mode, the command never removes files when\n+\trestoring. In no-overlay mode, tracked files that do not\n+\tappear in the `--source` tree are removed, to make them match\n+\t`<tree>` exactly. The default is no-overlay mode.\n+\n+EXAMPLES\n+--------\n+\n+The following sequence switches to the `master` branch, reverts the\n+`Makefile` to two revisions back, deletes hello.c by mistake, and gets\n+it back from the index.\n+\n+------------\n+$ git switch master\n+$ git restore --source master~2 Makefile  <1>\n+$ rm -f hello.c\n+$ git restore hello.c                     <2>\n+------------\n+\n+<1> take a file out of another commit\n+<2> restore hello.c from the index\n+\n+If you want to restore _all_ C source files to match the version in\n+the index, you can say\n+\n+------------\n+$ git restore '*.c'\n+------------\n+\n+Note the quotes around `*.c`.  The file `hello.c` will also be\n+restored, even though it is no longer in the working tree, because the\n+file globbing is used to match entries in the index (not in the\n+working tree by the shell).\n+\n+To restore all files in the current directory\n+\n+------------\n+$ git restore .\n+------------\n+\n+or to restore all working tree files with 'top' pathspec magic (see\n+linkgit:gitglossary[7])\n+\n+------------\n+$ git restore :/\n+------------\n+\n+To restore a file in the index to match the version in `HEAD` (this is\n+the same as using linkgit:git-reset[1])\n+\n+------------\n+$ git restore --staged hello.c\n+------------\n+\n+or you can restore both the index and the working tree (this the same\n+as using linkgit:git-checkout[1])\n+\n+------------\n+$ git restore --source=HEAD --staged --worktree hello.c\n+------------\n+\n+or the short form which is more practical but less readable:\n+\n+------------\n+$ git restore -s@ -SW hello.c\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1],\n+linkgit:git-reset[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..018ecf49d3 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -30,6 +30,9 @@ should see linkgit:git-checkout[1], specifically the `git checkout\n <commit> -- <filename>` syntax.  Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n+\n OPTIONS\n -------\n <commit>...::\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 00156d64aa..fbed007354 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -210,6 +210,26 @@ people via patch over e-mail.\n \n include::cmds-foreignscminterface.txt[]\n \n+Reset, restore and revert\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+There are three commands with similar names: `git reset`,\n+`git restore` and `git revert`.\n+\n+* linkgit:git-revert[1] is about making a new commit that reverts the\n+  changes made by other commits.\n+\n+* linkgit:git-restore[1] is about restoring files in the working tree\n+  from either the index or another commit. This command does not\n+  update your branch. The command can also be used to restore files in\n+  the index from another commit.\n+\n+* linkgit:git-reset[1] is about updating your branch, moving the tip\n+  in order to add or remove commits from the branch. This operation\n+  changes the commit history.\n++\n+`git reset` can also be used to restore the index, overlapping with\n+`git restore`.\n+\n \n Low-level commands (plumbing)\n -----------------------------\ndiff --git a/Makefile b/Makefile\nindex 8e91db73ad..ffe7e4f58f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n BUILT_INS += git-merge-subtree$X\n+BUILT_INS += git-restore$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\ndiff --git a/builtin.h b/builtin.h\nindex c64e44450e..6830000e14 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repack(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n+extern int cmd_restore(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0351735c6e..98dc2ded38 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -38,6 +38,11 @@ static const char * const switch_branch_usage[] = {\n \tNULL,\n };\n \n+static const char * const restore_usage[] = {\n+\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1622,3 +1627,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tFREE_AND_NULL(options);\n \treturn ret;\n }\n+\n+int cmd_restore(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_pathspec = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, restore_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 13317f47d4..b9eae1c258 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n+git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 39582cf511..6d439e723f 100644\n--- a/git.c\n+++ b/git.c\n@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373637","messageId":"20190411131218.19195-3-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 02/16] restore: take tree-ish from --source option instead","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:04Z","receivedAt":"2019-04-11T13:12:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is another departure from 'git checkout' syntax, which uses -- to\nseparate ref and pathspec. The observation is restore (or \"git\ncheckout -- <pathspec>\") is most often used to restore some files from\nthe index. If this is correct, we can simplify it by taking away the\nref, so that we can write\n\n    git restore some-file\n\nwithout worrying about some-file being a ref and whether we need to do\n\n    git restore -- some-file\n\nfor safety. If the source of the restore comes from a tree, it will be\nin the form of an option with value, e.g.\n\n    git restore --source=this-tree some-file\n\nThis is of course longer to type than using \"--\". But hopefully it\nwill not be used as often, and it is clearly easier to understand.\n\ndwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\nbecause it's irrelevant because we don't really care about dwim-ing.\nWith accept_ref being unset, dwim can't happen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------\n 1 file changed, 34 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 98dc2ded38..5aba345712 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -39,7 +39,7 @@ static const char * const switch_branch_usage[] = {\n };\n \n static const char * const restore_usage[] = {\n-\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tN_(\"git restore [<options>] [--source=<branch>] <file>...\"),\n \tNULL,\n };\n \n@@ -59,6 +59,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint dwim_new_local_branch;\n \tint discard_changes;\n+\tint accept_ref;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n@@ -76,6 +77,7 @@ struct checkout_opts {\n \tint branch_exists;\n \tconst char *prefix;\n \tstruct pathspec pathspec;\n+\tconst char *from_treeish;\n \tstruct tree *source_tree;\n };\n \n@@ -1410,6 +1412,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n+\tint parseopt_flags = 0;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1421,8 +1424,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\targc = parse_options(argc, argv, prefix, options, usagestr,\n-\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\tif (!opts->accept_pathspec && !opts->accept_ref)\n+\t\tBUG(\"make up your mind, you need to take _something_\");\n+\tif (opts->accept_pathspec && opts->accept_ref)\n+\t\tparseopt_flags = PARSE_OPT_KEEP_DASHDASH;\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     usagestr, parseopt_flags);\n \n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n@@ -1481,7 +1489,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t * including \"last branch\" syntax and DWIM-ery for names of\n \t * remote branches, erroring out for invalid or ambiguous cases.\n \t */\n-\tif (argc) {\n+\tif (argc && opts->accept_ref) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n@@ -1493,6 +1501,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n+\t} else if (!opts->accept_ref && opts->from_treeish) {\n+\t\tstruct object_id rev;\n+\n+\t\tif (get_oid_mb(opts->from_treeish, &rev))\n+\t\t\tdie(_(\"could not resolve %s\"), opts->from_treeish);\n+\n+\t\tsetup_new_branch_info_and_source_tree(&new_branch_info,\n+\t\t\t\t\t\t      opts, &rev,\n+\t\t\t\t\t\t      opts->from_treeish);\n+\n+\t\tif (!opts->source_tree)\n+\t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n \tif (argc) {\n@@ -1576,6 +1596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.only_merge_on_switching_branches = 0;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n@@ -1611,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n@@ -1631,15 +1653,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n int cmd_restore(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option restore_options[] = {\n+\t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n+\t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.dwim_new_local_branch = 1;\n-\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373638","messageId":"20190411131218.19195-4-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 03/16] restore: make pathspec mandatory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:05Z","receivedAt":"2019-04-11T13:12:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore\" without arguments does not make much sense when\nit's about restoring files (what files now?). We could default to\neither\n\n    git restore .\n\nor\n\n    git restore :/\n\nNeither is intuitive. Make the user always give pathspec, force the\nuser to think the scope of restore they want because this is a\ndestructive operation.\n\n\"git restore -p\" without pathspec is an exception to this\nbecause it really is a separate mode. It will be treated as running\npatch mode on the whole worktree.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5aba345712..77db5236f0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -65,6 +65,7 @@ struct checkout_opts {\n \tint only_merge_on_switching_branches;\n \tint can_switch_when_in_progress;\n \tint orphan_from_empty_tree;\n+\tint empty_pathspec_ok;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1515,6 +1516,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\tif (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&\n+\t    !opts->patch_mode)\t/* patch mode is special */\n+\t\tdie(_(\"you must specify path(s) to restore\"));\n+\n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n \t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n@@ -1601,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n \topts.orphan_from_empty_tree = 0;\n+\topts.empty_pathspec_ok = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1664,6 +1670,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n+\topts.empty_pathspec_ok = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373639","messageId":"20190411131218.19195-5-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 04/16] restore: disable overlay mode by default","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:06Z","receivedAt":"2019-04-11T13:13:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Overlay mode is considered confusing when the command is about\nrestoring files on worktree. Disable it by default. The user can still\nturn it on, or use 'git checkout' which still has overlay mode on by\ndefault.\n\nWhile at it, make the check in checkout_branch() stricter. Neither\n--overlay or --no-overlay should be accepted in branch switching mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 77db5236f0..9c5abe170e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1274,9 +1274,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n \t\t    \"--patch\");\n \n-\tif (!opts->overlay_mode)\n+\tif (opts->overlay_mode != -1)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n-\t\t    \"--no-overlay\");\n+\t\t    \"--[no]-overlay\");\n \n \tif (opts->writeout_stage)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n@@ -1399,7 +1399,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n@@ -1419,7 +1418,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n-\topts->overlay_mode = -1;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1593,6 +1591,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n \t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1607,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.can_switch_when_in_progress = 1;\n \topts.orphan_from_empty_tree = 0;\n \topts.empty_pathspec_ok = 1;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1645,6 +1645,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.implicit_detach = 0;\n \topts.can_switch_when_in_progress = 0;\n \topts.orphan_from_empty_tree = 1;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n@@ -1663,6 +1664,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1671,6 +1673,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n+\topts.overlay_mode = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373640","messageId":"20190411131218.19195-6-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 05/16] checkout: factor out worktree checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:07Z","receivedAt":"2019-04-11T13:13:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------\n 1 file changed, 59 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9c5abe170e..1753c8c5ed 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -330,17 +330,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,\n \t}\n }\n \n+static int checkout_worktree(const struct checkout_opts *opts)\n+{\n+\tstruct checkout state = CHECKOUT_INIT;\n+\tint nr_checkouts = 0, nr_unmerged = 0;\n+\tint errs = 0;\n+\tint pos;\n+\n+\tstate.force = 1;\n+\tstate.refresh_cache = 1;\n+\tstate.istate = &the_index;\n+\n+\tenable_delayed_checkout(&state);\n+\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\tif (ce->ce_flags & CE_MATCHED) {\n+\t\t\tif (!ce_stage(ce)) {\n+\t\t\t\terrs |= checkout_entry(ce, &state,\n+\t\t\t\t\t\t       NULL, &nr_checkouts);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (opts->writeout_stage)\n+\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n+\t\t\t\t\t\t       ce, pos,\n+\t\t\t\t\t\t       &state,\n+\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n+\t\t\telse if (opts->merge)\n+\t\t\t\terrs |= checkout_merged(pos, &state,\n+\t\t\t\t\t\t\t&nr_unmerged);\n+\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t}\n+\t}\n+\tremove_marked_cache_entries(&the_index, 1);\n+\tremove_scheduled_dirs();\n+\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n+\n+\tif (opts->count_checkout_paths) {\n+\t\tif (nr_unmerged)\n+\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n+\t\t\t\t\t      \"Recreated %d merge conflicts\",\n+\t\t\t\t\t      nr_unmerged),\n+\t\t\t\t   nr_unmerged);\n+\t\tif (opts->source_tree)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n+\t\t\t\t\t      \"Updated %d paths from %s\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts,\n+\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n+\t\t\t\t\t\t      DEFAULT_ABBREV));\n+\t\telse if (!nr_unmerged || nr_checkouts)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n+\t\t\t\t\t      \"Updated %d paths from the index\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts);\n+\t}\n+\n+\treturn errs;\n+}\n+\n static int checkout_paths(const struct checkout_opts *opts,\n \t\t\t  const char *revision)\n {\n \tint pos;\n-\tstruct checkout state = CHECKOUT_INIT;\n \tstatic char *ps_matched;\n \tstruct object_id rev;\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n-\tint nr_checkouts = 0, nr_unmerged = 0;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -426,53 +482,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\tstate.force = 1;\n-\tstate.refresh_cache = 1;\n-\tstate.istate = &the_index;\n-\n-\tenable_delayed_checkout(&state);\n-\tfor (pos = 0; pos < active_nr; pos++) {\n-\t\tstruct cache_entry *ce = active_cache[pos];\n-\t\tif (ce->ce_flags & CE_MATCHED) {\n-\t\t\tif (!ce_stage(ce)) {\n-\t\t\t\terrs |= checkout_entry(ce, &state,\n-\t\t\t\t\t\t       NULL, &nr_checkouts);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (opts->writeout_stage)\n-\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n-\t\t\t\t\t\t       ce, pos,\n-\t\t\t\t\t\t       &state,\n-\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n-\t\t\telse if (opts->merge)\n-\t\t\t\terrs |= checkout_merged(pos, &state,\n-\t\t\t\t\t\t\t&nr_unmerged);\n-\t\t\tpos = skip_same_name(ce, pos) - 1;\n-\t\t}\n-\t}\n-\tremove_marked_cache_entries(&the_index, 1);\n-\tremove_scheduled_dirs();\n-\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n-\n-\tif (opts->count_checkout_paths) {\n-\t\tif (nr_unmerged)\n-\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n-\t\t\t\t\t      \"Recreated %d merge conflicts\",\n-\t\t\t\t\t      nr_unmerged),\n-\t\t\t\t   nr_unmerged);\n-\t\tif (opts->source_tree)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n-\t\t\t\t\t      \"Updated %d paths from %s\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts,\n-\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n-\t\t\t\t\t\t      DEFAULT_ABBREV));\n-\t\telse if (!nr_unmerged || nr_checkouts)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n-\t\t\t\t\t      \"Updated %d paths from the index\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts);\n-\t}\n+\terrs |= checkout_worktree(opts);\n \n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373641","messageId":"20190411131218.19195-7-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 06/16] restore: add --worktree and --staged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:08Z","receivedAt":"2019-04-11T13:13:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"'git checkout <tree-ish> <pathspec>' updates both index and\nworktree. But updating the index when you want to restore worktree\nfiles is non-intuitive. The index contains the data ready for the next\ncommit, and there's no indication that the user will want to commit\nthe restored versions.\n\n'git restore' therefore by default only touches worktree. The user has\nthe option to update either the index with\n\n    git restore --staged --source=<tree> <path>  (1)\n\nor update both with\n\n    git restore --staged --worktree --source=<tree> <path> (2)\n\nPS. Orignally I wanted to make worktree update default and form (1)\nwould add index update while also updating the worktree, and the user\nwould need to do \"--staged --no-worktree\" to update index only. But it\nlooks really confusing that \"--staged\" option alone updates both. So\nnow form (2) is used for both, which reads much more obvious.\n\nPPS. Yes form (1) overlaps with \"git reset <rev> <path>\". I don't know\nif we can ever turn \"git reset\" back to \"_always_ reset HEAD and\noptionally do something else\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 68 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1753c8c5ed..e855c64cfe 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -66,6 +66,8 @@ struct checkout_opts {\n \tint can_switch_when_in_progress;\n \tint orphan_from_empty_tree;\n \tint empty_pathspec_ok;\n+\tint checkout_index;\n+\tint checkout_worktree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -397,6 +399,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tint checkout_index;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -422,9 +425,26 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n \t\t    opts->new_branch);\n \n-\tif (opts->patch_mode)\n-\t\treturn run_add_interactive(revision, \"--patch=checkout\",\n-\t\t\t\t\t   &opts->pathspec);\n+\tif (!opts->checkout_worktree && !opts->checkout_index)\n+\t\tdie(_(\"neither '%s' or '%s' is specified\"),\n+\t\t    \"--staged\", \"--worktree\");\n+\n+\tif (!opts->checkout_worktree && !opts->from_treeish)\n+\t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n+\t\t    \"--worktree\", \"--source\");\n+\n+\tif (opts->patch_mode) {\n+\t\tconst char *patch_mode;\n+\n+\t\tif (opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=checkout\";\n+\t\telse if (opts->checkout_index && !opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse\n+\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n+\t\t\t    \"--patch\", \"--worktree\");\n+\t\treturn run_add_interactive(revision, patch_mode, &opts->pathspec);\n+\t}\n \n \trepo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(&opts->pathspec) < 0)\n@@ -482,10 +502,30 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\terrs |= checkout_worktree(opts);\n+\tif (opts->checkout_worktree)\n+\t\terrs |= checkout_worktree(opts);\n \n-\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n-\t\tdie(_(\"unable to write new index file\"));\n+\t/*\n+\t * Allow updating the index when checking out from the index.\n+\t * This is to save new stat info.\n+\t */\n+\tif (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)\n+\t\tcheckout_index = 1;\n+\telse\n+\t\tcheckout_index = opts->checkout_index;\n+\n+\tif (checkout_index) {\n+\t\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n+\t\t\tdie(_(\"unable to write new index file\"));\n+\t} else {\n+\t\t/*\n+\t\t * NEEDSWORK: if --worktree is not specified, we\n+\t\t * should save stat info of checked out files in the\n+\t\t * index to avoid the next (potentially costly)\n+\t\t * refresh. But it's a bit tricker to do...\n+\t\t */\n+\t\trollback_lock_file(&lock_file);\n+\t}\n \n \tread_ref_full(\"HEAD\", 0, &rev, NULL);\n \thead = lookup_commit_reference_gently(the_repository, &rev, 1);\n@@ -1461,6 +1501,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n+\tif (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = 0;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = 0;\n+\t} else {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = -opts->checkout_index - 1;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = -opts->checkout_worktree - 1;\n+\t}\n+\tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n+\t\tBUG(\"these flags should be non-negative by now\");\n+\n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n@@ -1617,6 +1671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.orphan_from_empty_tree = 0;\n \topts.empty_pathspec_ok = 1;\n \topts.overlay_mode = -1;\n+\topts.checkout_index = -2;    /* default on */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1674,6 +1730,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL('S', \"staged\", &opts.checkout_index,\n+\t\t\t   N_(\"restore the index\")),\n+\t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n+\t\t\t   N_(\"restore the working tree (default)\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1684,6 +1744,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n \topts.overlay_mode = 0;\n+\topts.checkout_index = -1;    /* default off */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373642","messageId":"20190411131218.19195-8-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 07/16] restore: reject invalid combinations with --staged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:09Z","receivedAt":"2019-04-11T13:13:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-checkout rejects plenty of invalid option combinations. Since\ngit-checkout is equivalent of either\n\n    git restore --source --staged --worktree\n\nor\n\n    git restore --worktree\n\nthat still leaves the new mode 'git restore --index' unprotected. Reject\nsome more invalid option combinations.\n\nThe other new mode 'restore --source --worktree' does not need anything\nelse.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e855c64cfe..71e2589340 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -433,6 +433,16 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n \t\t    \"--worktree\", \"--source\");\n \n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->writeout_stage)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--ours\", \"--theirs\", \"--staged\");\n+\n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->merge)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--merge\", \"--conflict\", \"--staged\");\n+\n \tif (opts->patch_mode) {\n \t\tconst char *patch_mode;\n \n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373643","messageId":"20190411131218.19195-9-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 08/16] restore: default to --source=HEAD when only --staged is specified","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:10Z","receivedAt":"2019-04-11T13:13:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore --staged\" without --source does not make much sense since\nby default we restore from the index.  Instead of copying the index to\nitself, set the default source to HEAD in this case, yielding behavior\nthat matches \"git reset -- <paths>\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 71e2589340..09a03f1ff8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1524,6 +1524,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t}\n \tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n \t\tBUG(\"these flags should be non-negative by now\");\n+\t/*\n+\t * convenient shortcut: \"git restore --staged\" equals\n+\t * \"git restore --staged --source HEAD\"\n+\t */\n+\tif (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)\n+\t\topts->from_treeish = \"HEAD\";\n \n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373644","messageId":"20190411131218.19195-10-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 09/16] restore: replace --force with --ignore-unmerged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:11Z","receivedAt":"2019-04-11T13:13:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Use a more specific option name to express its purpose. --force may come\nback as an alias of --ignore-unmerged and possibly more. But since this\nis a destructive operation, I don't see why we need to \"force\" anything\nmore. We already don't hold back.\n\nWhen 'checkout --force' or 'restore --ignore-unmerged' is used, we may\nalso print warnings about unmerged entries being ignore. Since this is\nnot exactly warning (people tell us to do so), more informational, let\nit be suppressed if --quiet is given. This is a behavior change for\ngit-checkout.\n\nPS. The diff looks a bit iffy since --force is moved to\nadd_common_switch_branch_options() (i.e. for switching). But\ngit-checkout is also doing switching and inherits this --force.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 29 ++++++++++++++++++++---------\n 1 file changed, 20 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 09a03f1ff8..824ab65886 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -68,6 +68,8 @@ struct checkout_opts {\n \tint empty_pathspec_ok;\n \tint checkout_index;\n \tint checkout_worktree;\n+\tconst char *ignore_unmerged_opt;\n+\tint ignore_unmerged;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->new_branch_log)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-l\");\n \n-\tif (opts->force && opts->patch_mode)\n-\t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-f\");\n+\tif (opts->ignore_unmerged && opts->patch_mode)\n+\t\tdie(_(\"'%s' cannot be used with updating paths\"),\n+\t\t    opts->ignore_unmerged_opt);\n \n \tif (opts->force_detach)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--detach\");\n@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->merge && opts->patch_mode)\n \t\tdie(_(\"'%s' cannot be used with %s\"), \"--merge\", \"--patch\");\n \n-\tif (opts->force && opts->merge)\n-\t\tdie(_(\"'%s' cannot be used with %s\"), \"-f\", \"-m\");\n+\tif (opts->ignore_unmerged && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with %s\"),\n+\t\t    opts->ignore_unmerged_opt, \"-m\");\n \n \tif (opts->new_branch)\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n@@ -495,8 +499,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tif (ce->ce_flags & CE_MATCHED) {\n \t\t\tif (!ce_stage(ce))\n \t\t\t\tcontinue;\n-\t\t\tif (opts->force) {\n-\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n+\t\t\tif (opts->ignore_unmerged) {\n+\t\t\t\tif (!opts->quiet)\n+\t\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n \t\t\t} else if (opts->writeout_stage) {\n \t\t\t\terrs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);\n \t\t\t} else if (opts->merge) {\n@@ -1414,8 +1419,6 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n@@ -1433,6 +1436,8 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n@@ -1502,8 +1507,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n-\tif (opts->force)\n+\tif (opts->force) {\n \t\topts->discard_changes = 1;\n+\t\topts->ignore_unmerged_opt = \"--force\";\n+\t\topts->ignore_unmerged = 1;\n+\t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1750,6 +1758,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"restore the index\")),\n \t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n \t\t\t   N_(\"restore the working tree (default)\")),\n+\t\tOPT_BOOL(0, \"ignore-unmerged\", &opts.ignore_unmerged,\n+\t\t\t N_(\"ignore unmerged entries\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1762,6 +1772,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.overlay_mode = 0;\n \topts.checkout_index = -1;    /* default off */\n \topts.checkout_worktree = -2; /* default on */\n+\topts.ignore_unmerged_opt = \"--ignore-unmerged\";\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373645","messageId":"20190411131218.19195-11-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 10/16] restore: support --patch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:12Z","receivedAt":"2019-04-11T13:13:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-restore is different from git-checkout that it only restores the\nworktree by default, not both worktree and index. add--interactive\nneeds some update to support this mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c        |  6 +++--\n git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 56 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 824ab65886..bed79ae595 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -454,9 +454,11 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\tpatch_mode = \"--patch=checkout\";\n \t\telse if (opts->checkout_index && !opts->checkout_worktree)\n \t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse if (!opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=worktree\";\n \t\telse\n-\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n-\t\t\t    \"--patch\", \"--worktree\");\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(revision, patch_mode, &opts->pathspec);\n \t}\n \ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..3dfb3629c9 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -149,6 +149,20 @@ sub colored {\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@@ -1049,6 +1063,12 @@ sub color_diff {\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@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {\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@@ -1421,6 +1453,16 @@ sub display_hunks {\n \t\tdeletion => N__(\"Apply deletion 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\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\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n );\n \n sub patch_update_file {\n@@ -1756,6 +1798,16 @@ sub process_args {\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-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373646","messageId":"20190411131218.19195-12-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 11/16] t: add tests for restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:13Z","receivedAt":"2019-04-11T13:13:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/lib-patch-mode.sh               |  12 ++++\n t/t2070-restore.sh (new +x)       |  99 +++++++++++++++++++++++++++\n t/t2071-restore-patch.sh (new +x) | 110 ++++++++++++++++++++++++++++++\n 3 files changed, 221 insertions(+)\n\ndiff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh\nindex 06c3c91762..cfd76bf987 100644\n--- a/t/lib-patch-mode.sh\n+++ b/t/lib-patch-mode.sh\n@@ -2,28 +2,40 @@\n \n . ./test-lib.sh\n \n+# set_state <path> <worktree-content> <index-content>\n+#\n+# Prepare the content for path in worktree and the index as specified.\n set_state () {\n \techo \"$3\" > \"$1\" &&\n \tgit add \"$1\" &&\n \techo \"$2\" > \"$1\"\n }\n \n+# save_state <path>\n+#\n+# Save index/worktree content of <path> in the files _worktree_<path>\n+# and _index_<path>\n save_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tcat \"$1\" > _worktree_\"$noslash\" &&\n \tgit show :\"$1\" > _index_\"$noslash\"\n }\n \n+# set_and_save_state <path> <worktree-content> <index-content>\n set_and_save_state () {\n \tset_state \"$@\" &&\n \tsave_state \"$1\"\n }\n \n+# verify_state <path> <expected-worktree-content> <expected-index-content>\n verify_state () {\n \ttest \"$(cat \"$1\")\" = \"$2\" &&\n \ttest \"$(git show :\"$1\")\" = \"$3\"\n }\n \n+# verify_saved_state <path>\n+#\n+# Call verify_state with expected contents from the last save_state\n verify_saved_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tverify_state \"$1\" \"$(cat _worktree_\"$noslash\")\" \"$(cat _index_\"$noslash\")\"\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nnew file mode 100755\nindex 0000000000..73ea13ede9\n--- /dev/null\n+++ b/t/t2070-restore.sh\n@@ -0,0 +1,99 @@\n+#!/bin/sh\n+\n+test_description='restore basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\techo first-and-a-half >>first.t &&\n+\tgit add first.t &&\n+\ttest_commit second &&\n+\techo one >one &&\n+\techo two >two &&\n+\techo untracked >untracked &&\n+\techo ignored >ignored &&\n+\techo /ignored >.gitignore &&\n+\tgit add one two .gitignore &&\n+\tgit update-ref refs/heads/one master\n+'\n+\n+test_expect_success 'restore without pathspec is not ok' '\n+\ttest_must_fail git restore &&\n+\ttest_must_fail git restore --source=first\n+'\n+\n+test_expect_success 'restore a file, ignoring branch of same name' '\n+\tcat one >expected &&\n+\techo dirty >>one &&\n+\tgit restore one &&\n+\ttest_cmp expected one\n+'\n+\n+test_expect_success 'restore a file on worktree from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first first.t &&\n+\ttest_cmp expected first.t &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'restore a file in the index from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --staged first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore a file in both the index and worktree from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --staged --worktree first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore --staged uses HEAD as source' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob :./first.t >expected &&\n+\techo index-dirty >>first.t &&\n+\tgit add first.t &&\n+\tgit restore --staged first.t &&\n+\tgit cat-file blob :./first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '\n+\tgit init unmerged &&\n+\t(\n+\t\tcd unmerged &&\n+\t\techo one >unmerged &&\n+\t\techo one >common &&\n+\t\tgit add unmerged common &&\n+\t\tgit commit -m common &&\n+\t\tgit switch -c first &&\n+\t\techo first >unmerged &&\n+\t\tgit commit -am first &&\n+\t\tgit switch -c second master &&\n+\t\techo second >unmerged &&\n+\t\tgit commit -am second &&\n+\t\ttest_must_fail git merge first &&\n+\n+\t\techo dirty >>common &&\n+\t\ttest_must_fail git restore . &&\n+\n+\t\tgit restore --ignore-unmerged --quiet . >output 2>&1 &&\n+\t\tgit diff common >diff-output &&\n+\t\t: >empty &&\n+\t\ttest_cmp empty output &&\n+\t\ttest_cmp empty diff-output\n+\t)\n+'\n+\n+test_done\ndiff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\nnew file mode 100755\nindex 0000000000..98b2476e7c\n--- /dev/null\n+++ b/t/t2071-restore-patch.sh\n@@ -0,0 +1,110 @@\n+#!/bin/sh\n+\n+test_description='git restore --patch'\n+\n+. ./lib-patch-mode.sh\n+\n+test_expect_success PERL 'setup' '\n+\tmkdir dir &&\n+\techo parent >dir/foo &&\n+\techo dummy >bar &&\n+\tgit add bar dir/foo &&\n+\tgit commit -m initial &&\n+\ttest_tick &&\n+\ttest_commit second dir/foo head &&\n+\tset_and_save_state bar bar_work bar_index &&\n+\tsave_head\n+'\n+\n+test_expect_success PERL 'restore -p without pathspec is fine' '\n+\techo q >cmd &&\n+\tgit restore -p <cmd\n+'\n+\n+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'\n+\n+test_expect_success PERL 'saying \"n\" does nothing' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n n | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_saved_state dir/foo\n+'\n+\n+test_expect_success PERL 'git restore -p' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'git restore -p with staged changes' '\n+\tset_state dir/foo work index &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD^' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD^ &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent index\n+'\n+\n+test_expect_success PERL 'git restore -p handles deletion' '\n+\tset_state dir/foo work index &&\n+\trm dir/foo &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+# The idea in the rest is that bar sorts first, so we always say 'y'\n+# first and if the path limiter fails it'll apply to bar instead of\n+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in\n+# the failure case (and thus get out of the loop).\n+\n+test_expect_success PERL 'path limiting works: dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: -- dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | git restore -p --source=HEAD^ -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent head\n+'\n+\n+test_expect_success PERL 'path limiting works: foo inside dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | (cd dir && git restore -p foo) &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'none of this moved HEAD' '\n+\tverify_saved_head\n+'\n+\n+test_done\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373647","messageId":"20190411131218.19195-13-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 12/16] completion: support restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:14Z","receivedAt":"2019-04-11T13:13:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion for restore is straightforward. We could still do better\nthough by giving the list of just tracked files instead of all present\nones. But let's leave it for later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b24bc48276..58d18d41a2 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2491,6 +2491,21 @@ _git_reset ()\n \t__git_complete_refs\n }\n \n+_git_restore ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--source=*)\n+\t\t__git_complete_refs --cur=\"${cur##--source=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin restore\n+\t\t;;\n+\tesac\n+}\n+\n __git_revert_inprogress_options=\"--continue --quit --abort\"\n \n _git_revert ()\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373648","messageId":"20190411131218.19195-14-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:15Z","receivedAt":"2019-04-11T13:13:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since the operation in progress is merge, stick to the 'git merge'\nvariant of aborting. 'git reset --hard' does not really tell you about\naborting the merge by just looking, longer to type, and even though I\nknow by heart what --hard do, I still dislike it when I need to consider\nwhether --hard, --mixed or --soft.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/user-manual.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 94799faa2b..4e210970e1 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess\n away, you can always return to the pre-merge state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git merge --abort\n -------------------------------------------------\n \n Or, if you've already committed the merge that you want to throw away,\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373649","messageId":"20190411131218.19195-15-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 14/16] doc: promote \"git restore\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:16Z","receivedAt":"2019-04-11T13:14:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git restore\" (together with \"git switch\") are added\nto avoid the confusion of one-command-do-all \"git checkout\" for new\nusers. They are also helpful to avoid ambiguous context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands.\n\nOne nice thing about git-restore is the ability to restore\n\"everything\", so it can be used in \"git status\" advice instead of both\n\"git checkout\" and \"git reset\".  The three commands suggested by \"git\nstatus\" are add, rm and restore.\n\n\"git checkout\" is also removed from \"git help\" (i.e. it's no longer\nconsidered a commonly used command)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clean.txt        |  2 +-\n Documentation/git-commit.txt       |  2 +-\n Documentation/git-format-patch.txt |  2 +-\n Documentation/git-reset.txt        |  6 +--\n Documentation/git-revert.txt       |  4 +-\n Documentation/gitcli.txt           |  4 +-\n Documentation/giteveryday.txt      |  5 +-\n Documentation/gittutorial-2.txt    |  4 +-\n Documentation/gittutorial.txt      |  2 +-\n Documentation/user-manual.txt      | 12 ++---\n builtin/clone.c                    |  2 +-\n builtin/commit.c                   |  2 +-\n command-list.txt                   |  2 +-\n t/t7508-status.sh                  | 82 +++++++++++++++---------------\n t/t7512-status-help.sh             | 20 ++++----\n wt-status.c                        | 26 +++++++---\n 16 files changed, 93 insertions(+), 84 deletions(-)\n\ndiff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt\nindex 03056dad0d..8a31ccffea 100644\n--- a/Documentation/git-clean.txt\n+++ b/Documentation/git-clean.txt\n@@ -64,7 +64,7 @@ OPTIONS\n \tdirectory) and $GIT_DIR/info/exclude, but do still use the ignore\n \trules given with `-e` options.  This allows removing all untracked\n \tfiles, including build products.  This can be used (possibly in\n-\tconjunction with 'git reset') to create a pristine\n+\tconjunction with 'git restore' or 'git reset') to create a pristine\n \tworking directory to test a clean build.\n \n -X::\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex a85c2c2a4c..7628193284 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in\n your working tree are temporarily stored to a staging area\n called the \"index\" with 'git add'.  A file can be\n reverted back, only in the index but not in the working tree,\n-to that of the last commit with `git reset HEAD -- <file>`,\n+to that of the last commit with `git restore --staged <file>`,\n which effectively reverts 'git add' and prevents the changes to\n this file from participating in the next commit.  After building\n the state to be committed incrementally with these commands,\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 0a24a5679e..01bfcecf69 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:\n \n     $ git fetch <project> master:test-apply\n     $ git switch test-apply\n-    $ git reset --hard\n+    $ git restore --source=HEAD --staged --worktree :/\n     $ git am a.patch\n \n If it does not apply correctly, there can be various reasons.\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex c25f8a95b9..633d71d36a 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -29,9 +29,9 @@ This means that `git reset <paths>` is the opposite of `git add\n `git restore [--source=<tree-ish>] --staged <paths>...`.\n +\n After running `git reset <paths>` to update the index entry, you can\n-use linkgit:git-checkout[1] to check the contents out of the index to\n-the working tree.\n-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you\n+use linkgit:git-restore[1] to check the contents out of the index to\n+the working tree. Alternatively, using linkgit:git-restore[1]\n+and specifying a commit with `--source`, you\n can copy the contents of a path out of a commit to the index and to the\n working tree in one go.\n \ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 018ecf49d3..9aadc36881 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to\n throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n-should see linkgit:git-checkout[1], specifically the `git checkout\n-<commit> -- <filename>` syntax.  Take care with these alternatives as\n+should see linkgit:git-restore[1], specifically the `--source`\n+option. Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n See \"Reset, restore and revert\" in linkgit:git[1] for the differences\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 592e06d839..e2a9674297 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n    things:\n +\n --------------------------------\n-$ git checkout -- *.c\n-$ git checkout -- \\*.c\n+$ git restore *.c\n+$ git restore \\*.c\n --------------------------------\n +\n The former lets your shell expand the fileglob, and you are asking\ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex ad455f3e39..1bd919f92b 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -51,8 +51,7 @@ following commands.\n \n   * linkgit:git-commit[1] to advance the current branch.\n \n-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with\n-    pathname parameters) to undo changes.\n+  * linkgit:git-restore[1] to undo changes.\n \n   * linkgit:git-merge[1] to merge between local branches.\n \n@@ -82,7 +81,7 @@ Create a topic branch and develop.::\n ------------\n $ git switch -c alsa-audio <1>\n $ edit/compile/test\n-$ git checkout -- curses/ux_audio_oss.c <2>\n+$ git restore curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n $ edit/compile/test\n $ git diff HEAD <4>\ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e0976f6017..8bdb7d0bd3 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -370,13 +370,13 @@ situation:\n $ git status\n On branch master\n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   closing.txt\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   file.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex e6ad6b5f8d..59ef5cef1f 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -110,7 +110,7 @@ $ git status\n On branch master\n Changes to be committed:\n Your branch is up to date with 'origin/master'.\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   file1\n \tmodified:   file2\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 4e210970e1..8bce75b2cf 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed\n state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git restore --staged --worktree :/\n -------------------------------------------------\n \n If you make a commit that you later wish you hadn't, there are two\n@@ -1523,12 +1523,10 @@ Checking out an old version of a file\n \n In the process of undoing a previous bad change, you may find it\n useful to check out an older version of a particular file using\n-linkgit:git-checkout[1].  We've used `git checkout` before to switch\n-branches, but it has quite different behavior if it is given a path\n-name: the command\n+linkgit:git-restore[1]. The command\n \n -------------------------------------------------\n-$ git checkout HEAD^ path/to/file\n+$ git restore --source=HEAD^ path/to/file\n -------------------------------------------------\n \n replaces path/to/file by the contents it had in the commit HEAD^, and\n@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.\n The Workflow\n ------------\n \n-High-level operations such as linkgit:git-commit[1],\n-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data\n+High-level operations such as linkgit:git-commit[1] and\n+linkgit:git-restore[1] work by moving data\n between the working tree, the index, and the object database.  Git\n provides low-level operations which perform each of these steps\n individually.\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 50bde99618..024eb57996 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -492,7 +492,7 @@ static enum {\n static const char junk_leave_repo_msg[] =\n N_(\"Clone succeeded, but checkout failed.\\n\"\n    \"You can inspect what was checked out with 'git status'\\n\"\n-   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n+   \"and retry with 'git restore --source=HEAD :/'\\n\");\n \n static void remove_junk(void)\n {\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..fa5982cc86 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n-\t\t      \"not exceeded, and then \\\"git reset HEAD\\\" to recover.\"));\n+\t\t      \"not exceeded, and then \\\"git restore --staged :/\\\" to recover.\"));\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))\n \t\twrite_commit_graph_reachable(get_object_directory(), 0, 0);\ndiff --git a/command-list.txt b/command-list.txt\nindex b9eae1c258..cf8dccb439 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n git-check-ignore                        purehelpers\n git-check-mailmap                       purehelpers\n-git-checkout                            mainporcelain           history\n+git-checkout                            mainporcelain\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n git-cherry                              plumbinginterrogators          complete\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex e1f11293e2..681bc314b4 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -94,13 +94,13 @@ test_expect_success 'status --column' '\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -128,13 +128,13 @@ cat >expect <<\\EOF\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   ../dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   modified\n \n@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \t<GREEN>new file:   dir2/added<RESET>\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \t<RED>modified:   dir1/modified<RESET>\n \n@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   dir1/modified\n \n@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD^1 <file>...\" to unstage)\n+  (use \"git restore --source=HEAD^1 --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n   (commit or discard the untracked or modified content in submodules)\n \n \tmodified:   dir1/modified\n@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \tmodified:   sm (new commits)\n@@ -1379,13 +1379,13 @@ cat > expect << EOF\n ;   (use \"git pull\" to merge the remote branch into yours)\n ;\n ; Changes to be committed:\n-;   (use \"git reset HEAD <file>...\" to unstage)\n+;   (use \"git restore --staged <file>...\" to unstage)\n ;\n ;\tmodified:   sm\n ;\n ; Changes not staged for commit:\n ;   (use \"git add <file>...\" to update what will be committed)\n-;   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+;   (use \"git restore <file>...\" to discard changes in working directory)\n ;\n ;\tmodified:   dir1/modified\n ;\tmodified:   sm (new commits)\n@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 458608cc1e..1b9712c675 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -85,7 +85,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -110,7 +110,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -148,7 +148,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -176,7 +176,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\\''split_commit'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   to-revert.txt\n@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   to-revert.txt\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..19fd1add75 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t;\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \n \tif (!both_deleted) {\n@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t; /* NEEDSWORK: use \"git reset --unresolve\"??? */\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c\n+\t\t\t\t\t , _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n \telse\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n-\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n+\tstatus_printf_ln(s, c, _(\"  (use \\\"git restore <file>...\\\" to discard changes in working directory)\"));\n \tif (has_dirty_submodules)\n \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373650","messageId":"20190411131218.19195-16-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 15/16] rm: add --staged as alias for --cached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:17Z","receivedAt":"2019-04-11T13:14:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After the last patch, \"git status\" now suggests\n\n- add                  to stage some changes\n- restore [--worktree] to undo worktree changes\n- restore --staged     to undo index changes\n- rm --cached          to remove files from the index\n\nThis change is to make the suggestions for more consistent by using\n--staged across all suggested commands instead of the\nstill-a-bit-hard-to-understand --cached.\n\nPS. Should we suggest \"git stage\" instead of \"git add\"? Maybe that's\ngoing too far?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-rm.txt | 7 ++++---\n builtin/rm.c             | 7 ++++---\n t/t3600-rm.sh            | 6 +++---\n t/t7508-status.sh        | 2 +-\n wt-status.c              | 4 ++--\n 5 files changed, 14 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex b5c46223c4..4271fc5eaa 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,7 +8,7 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f | --force] [-n] [-r] [--staged] [--ignore-unmatch] [--quiet] [--] <file>...\n \n DESCRIPTION\n -----------\n@@ -55,10 +55,11 @@ OPTIONS\n \tthe list of files, (useful when filenames might be mistaken\n \tfor command-line options).\n \n+--staged::\n --cached::\n \tUse this option to unstage and remove paths only from the index.\n-\tWorking tree files, whether modified or not, will be\n-\tleft alone.\n+\tWorking tree files, whether modified or not, will be left\n+\talone. `--cached` is synonym for `--staged`.\n \n --ignore-unmatch::\n \tExit with a zero status even if no files matched.\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex db85b33982..47c8eb100b 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -217,7 +217,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t\t     \"staged in the index:\",\n \t\t\t     \"the following files have changes \"\n \t\t\t     \"staged in the index:\", files_cached.nr),\n-\t\t\t  _(\"\\n(use --cached to keep the file,\"\n+\t\t\t  _(\"\\n(use --staged to keep the file,\"\n \t\t\t    \" or -f to force removal)\"),\n \t\t\t  &errs);\n \tstring_list_clear(&files_cached, 0);\n@@ -226,7 +226,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t\t  Q_(\"the following file has local modifications:\",\n \t\t\t     \"the following files have local modifications:\",\n \t\t\t     files_local.nr),\n-\t\t\t  _(\"\\n(use --cached to keep the file,\"\n+\t\t\t  _(\"\\n(use --staged to keep the file,\"\n \t\t\t    \" or -f to force removal)\"),\n \t\t\t  &errs);\n \tstring_list_clear(&files_local, 0);\n@@ -240,7 +240,8 @@ static int ignore_unmatch = 0;\n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n \tOPT__QUIET(&quiet, N_(\"do not list removed files\")),\n-\tOPT_BOOL( 0 , \"cached\",         &index_only, N_(\"only remove from the index\")),\n+\tOPT_BOOL( 0 , \"staged\",         &index_only, N_(\"only remove from the index\")),\n+\tOPT_BOOL( 0 , \"cached\",         &index_only, N_(\"synonym for --staged\")),\n \tOPT__FORCE(&force, N_(\"override the up-to-date check\"), PARSE_OPT_NOCOMPLETE),\n \tOPT_BOOL('r', NULL,             &recursive,  N_(\"allow recursive removal\")),\n \tOPT_BOOL( 0 , \"ignore-unmatch\", &ignore_unmatch,\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 04e5d42bd3..5686032b8c 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -797,7 +797,7 @@ test_expect_success 'rm file with local modification' '\n \tcat >expect <<-\\EOF &&\n \terror: the following file has local modifications:\n \t    foo.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \tgit commit -m \"testing rm 3\" &&\n \techo content3 >foo.txt &&\n@@ -819,7 +819,7 @@ test_expect_success 'rm file with changes in the index' '\n \tcat >expect <<-\\EOF &&\n \terror: the following file has changes staged in the index:\n \t    foo.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \tgit reset --hard &&\n \techo content5 >foo.txt &&\n@@ -845,7 +845,7 @@ test_expect_success 'rm files with two different errors' '\n \t(use -f to force removal)\n \terror: the following file has changes staged in the index:\n \t    bar1.txt\n-\t(use --cached to keep the file, or -f to force removal)\n+\t(use --staged to keep the file, or -f to force removal)\n \tEOF\n \techo content >foo1.txt &&\n \tgit add foo1.txt &&\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex 681bc314b4..738f3df2f9 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -71,7 +71,7 @@ test_expect_success 'setup' '\n '\n \n test_expect_success 'status (1)' '\n-\ttest_i18ngrep \"use \\\"git rm --cached <file>\\.\\.\\.\\\" to unstage\" output\n+\ttest_i18ngrep \"use \\\"git rm --staged <file>\\.\\.\\.\\\" to unstage\" output\n '\n \n strip_comments () {\ndiff --git a/wt-status.c b/wt-status.c\nindex 19fd1add75..4d065ce89e 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -187,7 +187,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n \t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n \t\t\t\t\t s->reference);\n \t} else\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n+\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --staged <file>...\\\" to unstage)\"));\n \n \tif (!both_deleted) {\n \t\tif (!del_mod_conflict)\n@@ -220,7 +220,7 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)\n \t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n \t\t\t\t\t s->reference);\n \t} else\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n+\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --staged <file>...\\\" to unstage)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373651","messageId":"20190411131218.19195-17-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v2 16/16] help: move git-diff and git-reset to different groups","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-11T13:12:18Z","receivedAt":"2019-04-11T13:14:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The third column in command-list.txt determines what group a common\ncommand is printed in 'git help'.\n\n\"git reset\" is currently in the \"work on the current change (see also:\ngit help everyday)\" group. While it's true that \"git reset\" can\nmanipulate the index and can be in this group, its unique\nfunctionality is resetting HEAD, which should be the \"grow, mark,\ntweak history\" group.\n\nMoving it there will also avoid the confusion because both 'restore'\nand 'reset' are in the same group, next to each other.\n\nWhile looking at the 'group, mark, tweak history', I realize \"git\ndiff\" should not be there. All the commands in this group is about\n_changing_ the commit history while \"git diff\" is a read-only\noperation. It fits better in the \"examine the history and state\" group\n(especially when \"git status\", its close friend, is already there).\n\nThis is what we have after the reorganization:\n\n    work on the current change (see also: git help everyday)\n       add       Add file contents to the index\n       mv        Move or rename a file, a directory, or a symlink\n       restore   Restore working tree files\n       rm        Remove files from the working tree and from the index\n\n    examine the history and state (see also: git help revisions)\n       bisect    Use binary search to find the commit that introduced a bug\n       diff      Show changes between commits, commit and working tree, etc\n       grep      Print lines matching a pattern\n       log       Show commit logs\n       show      Show various types of objects\n       status    Show the working tree status\n\n    grow, mark and tweak your common history\n       branch    List, create, or delete branches\n       commit    Record changes to the repository\n       merge     Join two or more development histories together\n       rebase    Reapply commits on top of another base tip\n       reset     Reset current HEAD to the specified state\n       switch    Switch branches\n       tag       Create, list, delete or verify a tag object signed with GPG\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex cf8dccb439..a9ac72bef4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface\n git-cvsserver                           foreignscminterface\n git-daemon                              synchingrepositories\n git-describe                            mainporcelain\n-git-diff                                mainporcelain           history\n+git-diff                                mainporcelain           info\n git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff-tree                           plumbinginterrogators\n@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n-git-reset                               mainporcelain           worktree\n+git-reset                               mainporcelain           history\n git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n-- \n2.21.0.682.g30d2204636\n\n"},{"id":"373709","messageId":"xmqq4l73ztxf.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-12T05:14:36Z","receivedAt":"2019-04-12T05:14:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> v2 should address all the comments from v1. I still haven't done that\n> --intent-to-add thing yet even though I'd like to make it default\n> behavior too. Anyway changes are\n>\n> - --index is renamed to --staged to avoid conflict with --index from\n>   git-apply, which has different meaning.\n\nI think this is a reasonable compromise.  Documentation/gitcli.txt\nneeds to be updated for this new addition, where it currently only\ntalks about the distinction between \"--cached\" and \"--index\".\n\n> - git-rm learns about --staged as an alias of --cached (in fact it's\n>   more the other way around). This is to keep suggestions consistent\n>   because we tell people to do \"git foo --staged\" everywhere.\n\nI am not sure 100% about this one.  If \"--staged\" is only about\nupdating the index without touching the working tree, then existing\n\"--cached\" is already serving its purpose and there is no need to\nadd yet another.  Why did we need it for \"restore-paths\" in the\nfirst place?  Is it because the target of restoring can be one of\nthe three (index+working-tree, index-only, or working-tree only),\nnot just the traditional two (index+working-tree that is signalled\nby \"--index\", and index-only that is signalled by \"--cached\")?\n\nI think it would make sense to introduce --staged to \"git rm\", if we\nteach the third target (i.e. \"working-tree only\") to the command,\nbut otherwise, I am not sure if it would help reducing conflusion.\n"},{"id":"373793","messageId":"CACsJy8A42eObPkDvuPd7GodKxfaRMk0cd1YLUz+G-K4Qed2X7Q@mail.gmail.com","threadId":"50690","inReplyTo":"xmqq4l73ztxf.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-13T10:39:28Z","receivedAt":"2019-04-13T10:39:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Apr 12, 2019 at 12:14 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > - git-rm learns about --staged as an alias of --cached (in fact it's\n> >   more the other way around). This is to keep suggestions consistent\n> >   because we tell people to do \"git foo --staged\" everywhere.\n>\n> I am not sure 100% about this one.  If \"--staged\" is only about\n> updating the index without touching the working tree, then existing\n> \"--cached\" is already serving its purpose and there is no need to\n> add yet another.\n\nIt's so we suggest the same option name in \"git status\" instead of\n\n - git add/rm/retore <paths>\n - git restore [--source] --staged <paths>\n - git rm --cached <paths>\n\nIt's in the name of consistency but perhaps it's just not worth it? It\ncould also be seen as the continuation of adding --staged [1]\n\n[1] https://public-inbox.org/git/1225296936-1357-1-git-send-email-dsymonds@gmail.com/\n\n> Why did we need it for \"restore-paths\" in the\n> first place?  Is it because the target of restoring can be one of\n> the three (index+working-tree, index-only, or working-tree only),\n> not just the traditional two (index+working-tree that is signalled\n> by \"--index\", and index-only that is signalled by \"--cached\")?\n\nYes.\n\n> I think it would make sense to introduce --staged to \"git rm\", if we\n> teach the third target (i.e. \"working-tree only\") to the command,\n> but otherwise, I am not sure if it would help reducing conflusion.\n\nI could add that. \"git rm --worktree\" then is almost like \"rm\" except\nthat only tracked paths are affected. The default target of \"git rm\"\nis still \"both worktree and index\" though. I don't think we can ever\nchange that (e.g. to match \"git restore\" which defaults to worktree\nonly)\n-- \nDuy\n"},{"id":"374016","messageId":"CACsJy8A9xfR7FheOHUoywNz_D7W1X1fv=mUBz-uu82CGZqYfVA@mail.gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-17T10:04:26Z","receivedAt":"2019-04-17T10:04:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> This is the companion of \"git switch\" and is based on that topic.\n> This command peforms the \"checkout paths\" from git-checkout, git-reset\n> and also has a third mode to reset only worktree, leaving the index\n> alone.\n\nIt does not have to be done now. But I'm just wondering, does anyone\nthink adding --dry-run is a good idea? This command is destructive by\ndefault, so careful people might want it, I dunno.\n-- \nDuy\n"},{"id":"374078","messageId":"xmqqa7gojggg.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"CACsJy8A9xfR7FheOHUoywNz_D7W1X1fv=mUBz-uu82CGZqYfVA@mail.gmail.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-18T00:38:07Z","receivedAt":"2019-04-18T00:39:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>>\n>> This is the companion of \"git switch\" and is based on that topic.\n>> This command peforms the \"checkout paths\" from git-checkout, git-reset\n>> and also has a third mode to reset only worktree, leaving the index\n>> alone.\n>\n> It does not have to be done now. But I'm just wondering, does anyone\n> think adding --dry-run is a good idea? This command is destructive by\n> default, so careful people might want it, I dunno.\n\nYeah, \"give --dry-run for anything potentially destructive\" may be a\ngood general principle, although we'd need to know where to stop.\nFor example, I am not sure if \"git reset --hard -n\" would make all\nthat much sense, as the sole point of running \"reset --hard\" is \"I\nmade an unrecoverable mess---get me back to a clean and known state\nno matter what\" and in that context, \"let me see which files in the\nworking tree will be removed and checked out of the tree-ish and\nwhich paths in the index records a different blob than what is in\nthe tree-ish, before deciding if I still want to stay in the\nunrecoverable mess or I want to get out of it\" is something you\ncould request, but at the same time, there is not much point in\nactually asking it.  Of course, a --dry-run that is not used often\nsimply because it is not all that useful in practice is fine to have\nas long as it works correctly---I am saying that it's not something\nI'd personally prioritize myself.\n\nIt would be an excellent addition to \"restore-path\" (and also to\n\"checkout [<tree-ish> [--]] pathspec\") to give \"--dry-run\".  Not\njust because it is destructive, but because unlike \"reset --hard\",\nit is selectively destructive.  Having a way to make sure that the\ngiven pathspec touches only the paths that the user intends to\nrecover from the tree-ish or the index would be valuable.\n\nBut it is a new feature, and I'd think it can (and probably should)\nbe done as a follow-on series outside the main series you have been\nworking on.  Let's make sure that we have the basics ready by the\nend of this cycle.\n\nThanks.\n\n\n\n"},{"id":"374095","messageId":"CACsJy8CSQDpDteYUH86n-e+sXfqVGmvV3pgminunZgKOVQNivg@mail.gmail.com","threadId":"50690","inReplyTo":"xmqqa7gojggg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-18T09:40:56Z","receivedAt":"2019-04-18T09:41:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 18, 2019 at 7:38 AM Junio C Hamano <gitster@pobox.com> wrote:\n> It would be an excellent addition to \"restore-path\" (and also to\n> \"checkout [<tree-ish> [--]] pathspec\") to give \"--dry-run\".  Not\n> just because it is destructive, but because unlike \"reset --hard\",\n> it is selectively destructive.  Having a way to make sure that the\n> given pathspec touches only the paths that the user intends to\n> recover from the tree-ish or the index would be valuable.\n\nYep. I thought about that complicated pathspec stuff but forgot to\nmention. When you start using wildcards and magic, it's sometimes hard\nto predict the outcome on the first try.\n\nAlso about git-checkout but that's kinda weird. git-checkout has two\nfaces, the switching business is \"safety first\" and --dry-run does not\nmake sense. The restoring paths can make use of --dry-run, but then\nwe'll need to describe that this option only applies to one of the two\ncases, urghhh\n\n> But it is a new feature, and I'd think it can (and probably should)\n> be done as a follow-on series outside the main series you have been\n> working on.  Let's make sure that we have the basics ready by the\n> end of this cycle.\n\nYeah, it's not going to happen now. I will also drop \"rm --staged\" in\nthe next reroll to stop expanding the scope of this series. \"rm\n[--staged] [--worktree]\" may come back, but now is not its time.\n-- \nDuy\n"},{"id":"374099","messageId":"nycvar.QRO.7.76.6.1904181201180.46@tvgsbejvaqbjf.bet","threadId":"50690","inReplyTo":"xmqqa7gojggg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-04-18T10:03:25Z","receivedAt":"2019-04-18T10:03:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 18 Apr 2019, Junio C Hamano wrote:\n\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> > On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >>\n> >> This is the companion of \"git switch\" and is based on that topic.\n> >> This command peforms the \"checkout paths\" from git-checkout, git-reset\n> >> and also has a third mode to reset only worktree, leaving the index\n> >> alone.\n> >\n> > It does not have to be done now. But I'm just wondering, does anyone\n> > think adding --dry-run is a good idea? This command is destructive by\n> > default, so careful people might want it, I dunno.\n>\n> Yeah, \"give --dry-run for anything potentially destructive\" may be a\n> good general principle, although we'd need to know where to stop.\n> For example, I am not sure if \"git reset --hard -n\" would make all\n> that much sense, as the sole point of running \"reset --hard\" is \"I\n> made an unrecoverable mess---get me back to a clean and known state\n> no matter what\" and in that context, \"let me see which files in the\n> working tree will be removed and checked out of the tree-ish and\n> which paths in the index records a different blob than what is in\n> the tree-ish, before deciding if I still want to stay in the\n> unrecoverable mess or I want to get out of it\" is something you\n> could request, but at the same time, there is not much point in\n> actually asking it.  Of course, a --dry-run that is not used often\n> simply because it is not all that useful in practice is fine to have\n> as long as it works correctly---I am saying that it's not something\n> I'd personally prioritize myself.\n>\n> It would be an excellent addition to \"restore-path\" (and also to\n> \"checkout [<tree-ish> [--]] pathspec\") to give \"--dry-run\".  Not\n> just because it is destructive, but because unlike \"reset --hard\",\n> it is selectively destructive.  Having a way to make sure that the\n> given pathspec touches only the paths that the user intends to\n> recover from the tree-ish or the index would be valuable.\n>\n> But it is a new feature, and I'd think it can (and probably should)\n> be done as a follow-on series outside the main series you have been\n> working on.  Let's make sure that we have the basics ready by the\n> end of this cycle.\n\nSince this command is supposed to make our not-quite-user-friendly\ncommand-line interface a lot more user-friendly, I think that we should\ntake a step back and think very hard about a way to make this recoverable\nan action.\n\nThe recently discussed project to make `git stash` handle unmerged index\nentries should be a good step in that direction.\n\nAnd I do not think that it would make a lot of sense to advance this\nfeature prematurely, i.e. without this safety hatch firmly in place.\n\nCiao,\nDscho\n"},{"id":"374101","messageId":"CACsJy8DR6gkzuvY71ka-4F0=1ZzHpc5pux7tCidqyw_dehqBuA@mail.gmail.com","threadId":"50690","inReplyTo":"nycvar.QRO.7.76.6.1904181201180.46@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-18T10:20:00Z","receivedAt":"2019-04-18T10:20:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 18, 2019 at 5:03 PM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> Hi,\n>\n> On Thu, 18 Apr 2019, Junio C Hamano wrote:\n>\n> > Duy Nguyen <pclouds@gmail.com> writes:\n> >\n> > > On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > >>\n> > >> This is the companion of \"git switch\" and is based on that topic.\n> > >> This command peforms the \"checkout paths\" from git-checkout, git-reset\n> > >> and also has a third mode to reset only worktree, leaving the index\n> > >> alone.\n> > >\n> > > It does not have to be done now. But I'm just wondering, does anyone\n> > > think adding --dry-run is a good idea? This command is destructive by\n> > > default, so careful people might want it, I dunno.\n> >\n> > Yeah, \"give --dry-run for anything potentially destructive\" may be a\n> > good general principle, although we'd need to know where to stop.\n> > For example, I am not sure if \"git reset --hard -n\" would make all\n> > that much sense, as the sole point of running \"reset --hard\" is \"I\n> > made an unrecoverable mess---get me back to a clean and known state\n> > no matter what\" and in that context, \"let me see which files in the\n> > working tree will be removed and checked out of the tree-ish and\n> > which paths in the index records a different blob than what is in\n> > the tree-ish, before deciding if I still want to stay in the\n> > unrecoverable mess or I want to get out of it\" is something you\n> > could request, but at the same time, there is not much point in\n> > actually asking it.  Of course, a --dry-run that is not used often\n> > simply because it is not all that useful in practice is fine to have\n> > as long as it works correctly---I am saying that it's not something\n> > I'd personally prioritize myself.\n> >\n> > It would be an excellent addition to \"restore-path\" (and also to\n> > \"checkout [<tree-ish> [--]] pathspec\") to give \"--dry-run\".  Not\n> > just because it is destructive, but because unlike \"reset --hard\",\n> > it is selectively destructive.  Having a way to make sure that the\n> > given pathspec touches only the paths that the user intends to\n> > recover from the tree-ish or the index would be valuable.\n> >\n> > But it is a new feature, and I'd think it can (and probably should)\n> > be done as a follow-on series outside the main series you have been\n> > working on.  Let's make sure that we have the basics ready by the\n> > end of this cycle.\n>\n> Since this command is supposed to make our not-quite-user-friendly\n> command-line interface a lot more user-friendly, I think that we should\n> take a step back and think very hard about a way to make this recoverable\n> an action.\n\nbackup-log can handle that (or at least provide the foundation) and it\neven allows to recover from accidental \"git reset --hard\". But since\nI'm not going to resubmit that again (I'm done with thinking hard\nabout \"git undo\" or overwriting ignored files, I do not have the\nanswer), I'll let --dry-run rest in peace.\n\n> The recently discussed project to make `git stash` handle unmerged index\n> entries should be a good step in that direction.\n>\n> And I do not think that it would make a lot of sense to advance this\n> feature prematurely, i.e. without this safety hatch firmly in place.\n>\n> Ciao,\n> Dscho\n-- \nDuy\n"},{"id":"374461","messageId":"20190425094600.15673-1-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190411131218.19195-1-pclouds@gmail.com","subject":"[PATCH v3 00/16] Add new command 'restore'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:44Z","receivedAt":"2019-04-25T09:46:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 changes are really small\n\n- gitcli.txt is updated to mention the --staged/--worktree pair, in\n  comparison to --cached/--index which is also mention there.\n\n- The patch 'rm --staged' is dropped. It could come back. But if it\n  does, it should be in a series where commands that take\n  --cached/--index will now take both --staged/--worktree. Those are\n  'rm', 'apply', 'check-attr', 'grep', 'diff' and maybe 'ls-files'.\n  \n  In other words we support --staged/--worktree everywhere while\n  --cached/--index still remains because of muscle memory. This is\n  of course only a good move if --staged/--worktree is much better\n  than --cached/--index.\n\n- Since there's a chance this series may end up in 'master' and get\n  released, and I'm still worried that I screwed up somewhere, the last\n  patch declares the two commands experimental for maybe one or two\n  release cycles.\n  \n  If the reception is good, we revert that patch. If something\n  horrible is found, we can still change the default behavior without\n  anybody yelling at us. Or worst case scenario, we remove both\n  commands and declare a failed experiment.\n\nPS. the intent-to-add support is still not in. But it should be in\nbefore the experimental status is removed.\n\nNguyễn Thái Ngọc Duy (16):\n  checkout: split part of it to new command 'restore'\n  restore: take tree-ish from --source option instead\n  restore: make pathspec mandatory\n  restore: disable overlay mode by default\n  checkout: factor out worktree checkout code\n  restore: add --worktree and --staged\n  restore: reject invalid combinations with --staged\n  restore: default to --source=HEAD when only --staged is specified\n  restore: replace --force with --ignore-unmerged\n  restore: support --patch\n  t: add tests for restore\n  completion: support restore\n  user-manual.txt: prefer 'merge --abort' over 'reset --hard'\n  doc: promote \"git restore\"\n  help: move git-diff and git-reset to different groups\n  Declare both git-switch and git-restore experimental\n\n .gitignore                             |   1 +\n Documentation/config/interactive.txt   |   3 +-\n Documentation/git-checkout.txt         |   3 +-\n Documentation/git-clean.txt            |   2 +-\n Documentation/git-commit.txt           |   2 +-\n Documentation/git-format-patch.txt     |   2 +-\n Documentation/git-reset.txt            |  13 +-\n Documentation/git-restore.txt (new)    | 185 +++++++++++++++\n Documentation/git-revert.txt           |   7 +-\n Documentation/git-switch.txt           |   2 +\n Documentation/git.txt                  |  20 ++\n Documentation/gitcli.txt               |  16 +-\n Documentation/giteveryday.txt          |   5 +-\n Documentation/gittutorial-2.txt        |   4 +-\n Documentation/gittutorial.txt          |   2 +-\n Documentation/user-manual.txt          |  14 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/checkout.c                     | 299 +++++++++++++++++++------\n builtin/clone.c                        |   2 +-\n builtin/commit.c                       |   2 +-\n command-list.txt                       |   7 +-\n contrib/completion/git-completion.bash |  15 ++\n git-add--interactive.perl              |  52 +++++\n git.c                                  |   1 +\n t/lib-patch-mode.sh                    |  12 +\n t/t2070-restore.sh (new +x)            |  99 ++++++++\n t/t2071-restore-patch.sh (new +x)      | 110 +++++++++\n t/t7508-status.sh                      |  82 +++----\n t/t7512-status-help.sh                 |  20 +-\n wt-status.c                            |  26 ++-\n 31 files changed, 850 insertions(+), 160 deletions(-)\n create mode 100644 Documentation/git-restore.txt\n create mode 100755 t/t2070-restore.sh\n create mode 100755 t/t2071-restore-patch.sh\n\nRange-diff dựa trên v2:\n 1:  41788fc2d2 !  1:  0d5ea2b7fe checkout: split part of it to new command 'restore'\n    @@ -342,6 +342,29 @@\n      Low-level commands (plumbing)\n      -----------------------------\n     \n    + diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\n    + --- a/Documentation/gitcli.txt\n    + +++ b/Documentation/gitcli.txt\n    +@@\n    + http://marc.info/?l=git&m=119150393620273 for further\n    + information.\n    + \n    ++Some other commands that also work on files in the working tree and/or\n    ++in the index can take `--staged` and/or `--worktree`.\n    ++\n    ++* `--staged` is exactly like `--cached`, which is used to ask a\n    ++  command to only work on the index, not the working tree.\n    ++\n    ++* `--worktree` is the opposite, to ask a command to work on the\n    ++  working tree only, not the index.\n    ++\n    ++* The two options can be specified together to ask a command to work\n    ++  on both the index and the working tree.\n    ++\n    + GIT\n    + ---\n    + Part of the linkgit:git[1] suite\n    +\n      diff --git a/Makefile b/Makefile\n      --- a/Makefile\n      +++ b/Makefile\n 2:  253dfb42ae =  2:  c3c6e7762a restore: take tree-ish from --source option instead\n 3:  0266c4d982 =  3:  0fbf963e7d restore: make pathspec mandatory\n 4:  ae4dd4c24e =  4:  2509bebf32 restore: disable overlay mode by default\n 5:  c51dd232c0 =  5:  206c507f7d checkout: factor out worktree checkout code\n 6:  6b19ddb1b3 =  6:  656bfcd659 restore: add --worktree and --staged\n 7:  3f1031cc23 =  7:  f2d3aa1027 restore: reject invalid combinations with --staged\n 8:  d6d9bed95c =  8:  57efad405d restore: default to --source=HEAD when only --staged is specified\n 9:  fca91f3cca =  9:  3c6b32c223 restore: replace --force with --ignore-unmerged\n10:  079273e2df = 10:  6665d7523b restore: support --patch\n11:  9d28d167fa = 11:  d7bda0c0cc t: add tests for restore\n12:  acd490f8b0 = 12:  e4622aff3d completion: support restore\n13:  336a7d8921 = 13:  787b0e485e user-manual.txt: prefer 'merge --abort' over 'reset --hard'\n14:  3b81b27255 = 14:  0df020c2c8 doc: promote \"git restore\"\n15:  ce7e890524 <  -:  ---------- rm: add --staged as alias for --cached\n16:  763aa1d6f1 = 15:  3302b1b0e1 help: move git-diff and git-reset to different groups\n -:  ---------- > 16:  ffeea858a7 Declare both git-switch and git-restore experimental\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374462","messageId":"20190425094600.15673-2-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 01/16] checkout: split part of it to new command 'restore'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:45Z","receivedAt":"2019-04-25T09:46:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Previously the switching branch business of 'git checkout' becomes a\nnew command 'switch'. This adds the restore command for the checking\nout paths path.\n\nSimilar to git-switch, a new man page is added to describe what the\ncommand will become. The implementation will be updated shortly to\nmatch the man page.\n\nA couple main differences from 'git checkout <paths>':\n\n- 'restore' by default will only update worktree. This matters more\n  when --source is specified ('checkout <tree> <paths>' updates both\n  worktree and index).\n\n- 'restore --staged' can be used to restore the index. This command\n  overlaps with 'git reset <paths>'.\n\n- both worktree and index could also be restored at the same time\n  (from a tree) when both --staged and --worktree are specified. This\n  overlaps with 'git checkout <tree> <paths>'\n\n- default source for restoring worktree and index is the index and\n  HEAD respectively. A different (tree) source could be specified as\n  with --source (*).\n\n- when both index and worktree are restored, --source must be\n  specified since the default source for these two individual targets\n  are different (**)\n\n- --no-overlay is enabled by default, if an entry is missing in the\n  source, restoring means deleting the entry\n\n(*) I originally went with --from instead of --source. I still think\n  --from is a better name. The short option -f however is already\n  taken by force. And I do think short option is good to have, e.g. to\n  write -s@ or -s@^ instead of --source=HEAD.\n\n(**) If you sit down and think about it, moving worktree's source from\n  the index to HEAD makes sense, but nobody is really thinking it\n  through when they type the commands.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                           |   1 +\n Documentation/config/interactive.txt |   3 +-\n Documentation/git-checkout.txt       |   3 +-\n Documentation/git-reset.txt          |   7 +-\n Documentation/git-restore.txt (new)  | 183 +++++++++++++++++++++++++++\n Documentation/git-revert.txt         |   3 +\n Documentation/git.txt                |  20 +++\n Documentation/gitcli.txt             |  12 ++\n Makefile                             |   1 +\n builtin.h                            |   1 +\n builtin/checkout.c                   |  26 ++++\n command-list.txt                     |   1 +\n git.c                                |   1 +\n 13 files changed, 257 insertions(+), 5 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex c687b92b1c..fb377106be 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -143,6 +143,7 @@\n /git-request-pull\n /git-rerere\n /git-reset\n+/git-restore\n /git-rev-list\n /git-rev-parse\n /git-revert\ndiff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt\nindex ad846dd7c9..a2d3c7ec44 100644\n--- a/Documentation/config/interactive.txt\n+++ b/Documentation/config/interactive.txt\n@@ -2,7 +2,8 @@ interactive.singleKey::\n \tIn interactive commands, allow the user to provide one-letter\n \tinput with a single key (i.e., without hitting enter).\n \tCurrently this is used by the `--patch` mode of\n-\tlinkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],\n+\tlinkgit:git-add[1], linkgit:git-checkout[1],\n+\tlinkgit:git-restore[1], linkgit:git-commit[1],\n \tlinkgit:git-reset[1], and linkgit:git-stash[1]. Note that this\n \tsetting is silently ignored if portable keystroke input\n \tis not available; requires the Perl module Term::ReadKey.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 58f18a0842..a294652dd6 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -570,7 +570,8 @@ $ git add frotz\n \n SEE ALSO\n --------\n-linkgit:git-switch[1]\n+linkgit:git-switch[1],\n+linkgit:git-restore[1]\n \n GIT\n ---\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex cbf901efb4..c25f8a95b9 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.\n \tthe current branch.)\n +\n This means that `git reset <paths>` is the opposite of `git add\n-<paths>`.\n+<paths>`. This command is equivalent to\n+`git restore [--source=<tree-ish>] --staged <paths>...`.\n +\n After running `git reset <paths>` to update the index entry, you can\n use linkgit:git-checkout[1] to check the contents out of the index to\n@@ -86,8 +87,8 @@ but carries forward unmerged index entries.\n \tchanges, reset is aborted.\n --\n \n-If you want to undo a commit other than the latest on a branch,\n-linkgit:git-revert[1] is your friend.\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n \n \n OPTIONS\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nnew file mode 100644\nindex 0000000000..b608f3f360\n--- /dev/null\n+++ b/Documentation/git-restore.txt\n@@ -0,0 +1,183 @@\n+git-restore(1)\n+==============\n+\n+NAME\n+----\n+git-restore - Restore working tree files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...\n+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+Restore specified paths in the working tree with some contents from a\n+restore source. If a path is tracked but does not exist in the restore\n+source, it will be removed to match the source.\n+\n+The command can also be used to restore the content in the index with\n+`--staged`, or restore both the working tree and the index with\n+`--staged --worktree`.\n+\n+By default, the restore sources for working tree and the index are the\n+index and `HEAD` respectively. `--source` could be used to specify a\n+commit as the restore source.\n+\n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n+\n+OPTIONS\n+-------\n+-s <tree>::\n+--source=<tree>::\n+\tRestore the working tree files with the content from the given\n+\ttree. It is common to specify the source tree by naming a\n+\tcommit, branch or tag associated with it.\n++\n+If not specified, the default restore source for the working tree is\n+the index, and the default restore source for the index index is\n+`HEAD`. When both `--staged` and `--worktree` are specified,\n+`--source` must also be specified.\n+\n+-p::\n+--patch::\n+\tInteractively select hunks in the difference between the\n+\trestore source and the restore location. See the ``Interactive\n+\tMode'' section of linkgit:git-add[1] to learn how to operate\n+\tthe `--patch` mode.\n++\n+Note that `--patch` can accept no pathspec and will prompt to restore\n+all modified paths.\n+\n+-W::\n+--worktree::\n+-S::\n+--staged::\n+\tSpecify the restore location. If neither option is specified,\n+\tby default the working tree is restored. Specifying `--staged`\n+\twill only restore the index. Specifying both restores both.\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages. Implies `--no-progress`.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+--ours::\n+--theirs::\n+\tWhen restoring files in the working tree from the index, use\n+\tstage #2 ('ours') or #3 ('theirs') for unmerged paths.\n++\n+Note that during `git rebase` and `git pull --rebase`, 'ours' and\n+'theirs' may appear swapped. See the explanation of the same options\n+in linkgit:git-checkout[1] for details.\n+\n+-m::\n+--merge::\n+\tWhen restoring files on the working tree from the index,\n+\trecreate the conflicted merge in the unmerged paths.\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values\n+\tare \"merge\" (default) and \"diff3\" (in addition to what is\n+\tshown by \"merge\" style, shows the original contents).\n+\n+--ignore-unmerged::\n+\tWhen restoring files on the working tree from the index, do\n+\tnot abort the operation if there are unmerged entries and\n+\tneither `--ours`, `--theirs`, `--merge` or `--conflict` is\n+\tspecified. Unmerged paths on the working tree are left alone.\n+\n+--ignore-skip-worktree-bits::\n+\tIn sparse checkout mode, by default is to only update entries\n+\tmatched by `<pathspec>` and sparse patterns in\n+\t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n+\tpatterns and unconditionally restores any files in\n+\t`<pathspec>`.\n+\n+--overlay::\n+--no-overlay::\n+\tIn overlay mode, the command never removes files when\n+\trestoring. In no-overlay mode, tracked files that do not\n+\tappear in the `--source` tree are removed, to make them match\n+\t`<tree>` exactly. The default is no-overlay mode.\n+\n+EXAMPLES\n+--------\n+\n+The following sequence switches to the `master` branch, reverts the\n+`Makefile` to two revisions back, deletes hello.c by mistake, and gets\n+it back from the index.\n+\n+------------\n+$ git switch master\n+$ git restore --source master~2 Makefile  <1>\n+$ rm -f hello.c\n+$ git restore hello.c                     <2>\n+------------\n+\n+<1> take a file out of another commit\n+<2> restore hello.c from the index\n+\n+If you want to restore _all_ C source files to match the version in\n+the index, you can say\n+\n+------------\n+$ git restore '*.c'\n+------------\n+\n+Note the quotes around `*.c`.  The file `hello.c` will also be\n+restored, even though it is no longer in the working tree, because the\n+file globbing is used to match entries in the index (not in the\n+working tree by the shell).\n+\n+To restore all files in the current directory\n+\n+------------\n+$ git restore .\n+------------\n+\n+or to restore all working tree files with 'top' pathspec magic (see\n+linkgit:gitglossary[7])\n+\n+------------\n+$ git restore :/\n+------------\n+\n+To restore a file in the index to match the version in `HEAD` (this is\n+the same as using linkgit:git-reset[1])\n+\n+------------\n+$ git restore --staged hello.c\n+------------\n+\n+or you can restore both the index and the working tree (this the same\n+as using linkgit:git-checkout[1])\n+\n+------------\n+$ git restore --source=HEAD --staged --worktree hello.c\n+------------\n+\n+or the short form which is more practical but less readable:\n+\n+------------\n+$ git restore -s@ -SW hello.c\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1],\n+linkgit:git-reset[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..018ecf49d3 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -30,6 +30,9 @@ should see linkgit:git-checkout[1], specifically the `git checkout\n <commit> -- <filename>` syntax.  Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n+See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n+between the three commands.\n+\n OPTIONS\n -------\n <commit>...::\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 00156d64aa..fbed007354 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -210,6 +210,26 @@ people via patch over e-mail.\n \n include::cmds-foreignscminterface.txt[]\n \n+Reset, restore and revert\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+There are three commands with similar names: `git reset`,\n+`git restore` and `git revert`.\n+\n+* linkgit:git-revert[1] is about making a new commit that reverts the\n+  changes made by other commits.\n+\n+* linkgit:git-restore[1] is about restoring files in the working tree\n+  from either the index or another commit. This command does not\n+  update your branch. The command can also be used to restore files in\n+  the index from another commit.\n+\n+* linkgit:git-reset[1] is about updating your branch, moving the tip\n+  in order to add or remove commits from the branch. This operation\n+  changes the commit history.\n++\n+`git reset` can also be used to restore the index, overlapping with\n+`git restore`.\n+\n \n Low-level commands (plumbing)\n -----------------------------\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 592e06d839..225513ef5e 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -209,6 +209,18 @@ See also http://marc.info/?l=git&m=116563135620359 and\n http://marc.info/?l=git&m=119150393620273 for further\n information.\n \n+Some other commands that also work on files in the working tree and/or\n+in the index can take `--staged` and/or `--worktree`.\n+\n+* `--staged` is exactly like `--cached`, which is used to ask a\n+  command to only work on the index, not the working tree.\n+\n+* `--worktree` is the opposite, to ask a command to work on the\n+  working tree only, not the index.\n+\n+* The two options can be specified together to ask a command to work\n+  on both the index and the working tree.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex 8e91db73ad..ffe7e4f58f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n BUILT_INS += git-merge-subtree$X\n+BUILT_INS += git-restore$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\ndiff --git a/builtin.h b/builtin.h\nindex c64e44450e..6830000e14 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repack(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n+extern int cmd_restore(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0351735c6e..98dc2ded38 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -38,6 +38,11 @@ static const char * const switch_branch_usage[] = {\n \tNULL,\n };\n \n+static const char * const restore_usage[] = {\n+\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1622,3 +1627,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tFREE_AND_NULL(options);\n \treturn ret;\n }\n+\n+int cmd_restore(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_pathspec = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, restore_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 13317f47d4..b9eae1c258 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n+git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 39582cf511..6d439e723f 100644\n--- a/git.c\n+++ b/git.c\n@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374463","messageId":"20190425094600.15673-3-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 02/16] restore: take tree-ish from --source option instead","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:46Z","receivedAt":"2019-04-25T09:46:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is another departure from 'git checkout' syntax, which uses -- to\nseparate ref and pathspec. The observation is restore (or \"git\ncheckout -- <pathspec>\") is most often used to restore some files from\nthe index. If this is correct, we can simplify it by taking away the\nref, so that we can write\n\n    git restore some-file\n\nwithout worrying about some-file being a ref and whether we need to do\n\n    git restore -- some-file\n\nfor safety. If the source of the restore comes from a tree, it will be\nin the form of an option with value, e.g.\n\n    git restore --source=this-tree some-file\n\nThis is of course longer to type than using \"--\". But hopefully it\nwill not be used as often, and it is clearly easier to understand.\n\ndwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\nbecause it's irrelevant because we don't really care about dwim-ing.\nWith accept_ref being unset, dwim can't happen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------\n 1 file changed, 34 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 98dc2ded38..5aba345712 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -39,7 +39,7 @@ static const char * const switch_branch_usage[] = {\n };\n \n static const char * const restore_usage[] = {\n-\tN_(\"git restore [<options>] [<branch>] -- <file>...\"),\n+\tN_(\"git restore [<options>] [--source=<branch>] <file>...\"),\n \tNULL,\n };\n \n@@ -59,6 +59,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint dwim_new_local_branch;\n \tint discard_changes;\n+\tint accept_ref;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n@@ -76,6 +77,7 @@ struct checkout_opts {\n \tint branch_exists;\n \tconst char *prefix;\n \tstruct pathspec pathspec;\n+\tconst char *from_treeish;\n \tstruct tree *source_tree;\n };\n \n@@ -1410,6 +1412,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n+\tint parseopt_flags = 0;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1421,8 +1424,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\targc = parse_options(argc, argv, prefix, options, usagestr,\n-\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\tif (!opts->accept_pathspec && !opts->accept_ref)\n+\t\tBUG(\"make up your mind, you need to take _something_\");\n+\tif (opts->accept_pathspec && opts->accept_ref)\n+\t\tparseopt_flags = PARSE_OPT_KEEP_DASHDASH;\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     usagestr, parseopt_flags);\n \n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n@@ -1481,7 +1489,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t * including \"last branch\" syntax and DWIM-ery for names of\n \t * remote branches, erroring out for invalid or ambiguous cases.\n \t */\n-\tif (argc) {\n+\tif (argc && opts->accept_ref) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n@@ -1493,6 +1501,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n+\t} else if (!opts->accept_ref && opts->from_treeish) {\n+\t\tstruct object_id rev;\n+\n+\t\tif (get_oid_mb(opts->from_treeish, &rev))\n+\t\t\tdie(_(\"could not resolve %s\"), opts->from_treeish);\n+\n+\t\tsetup_new_branch_info_and_source_tree(&new_branch_info,\n+\t\t\t\t\t\t      opts, &rev,\n+\t\t\t\t\t\t      opts->from_treeish);\n+\n+\t\tif (!opts->source_tree)\n+\t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n \tif (argc) {\n@@ -1576,6 +1596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.only_merge_on_switching_branches = 0;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n@@ -1611,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n@@ -1631,15 +1653,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n int cmd_restore(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option restore_options[] = {\n+\t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n+\t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.dwim_new_local_branch = 1;\n-\topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374464","messageId":"20190425094600.15673-4-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 03/16] restore: make pathspec mandatory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:47Z","receivedAt":"2019-04-25T09:46:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore\" without arguments does not make much sense when\nit's about restoring files (what files now?). We could default to\neither\n\n    git restore .\n\nor\n\n    git restore :/\n\nNeither is intuitive. Make the user always give pathspec, force the\nuser to think the scope of restore they want because this is a\ndestructive operation.\n\n\"git restore -p\" without pathspec is an exception to this\nbecause it really is a separate mode. It will be treated as running\npatch mode on the whole worktree.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5aba345712..77db5236f0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -65,6 +65,7 @@ struct checkout_opts {\n \tint only_merge_on_switching_branches;\n \tint can_switch_when_in_progress;\n \tint orphan_from_empty_tree;\n+\tint empty_pathspec_ok;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1515,6 +1516,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\tif (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&\n+\t    !opts->patch_mode)\t/* patch mode is special */\n+\t\tdie(_(\"you must specify path(s) to restore\"));\n+\n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n \t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n@@ -1601,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n \topts.orphan_from_empty_tree = 0;\n+\topts.empty_pathspec_ok = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1664,6 +1670,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n+\topts.empty_pathspec_ok = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374465","messageId":"20190425094600.15673-5-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 04/16] restore: disable overlay mode by default","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:48Z","receivedAt":"2019-04-25T09:46:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Overlay mode is considered confusing when the command is about\nrestoring files on worktree. Disable it by default. The user can still\nturn it on, or use 'git checkout' which still has overlay mode on by\ndefault.\n\nWhile at it, make the check in checkout_branch() stricter. Neither\n--overlay or --no-overlay should be accepted in branch switching mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 77db5236f0..9c5abe170e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1274,9 +1274,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n \t\t    \"--patch\");\n \n-\tif (!opts->overlay_mode)\n+\tif (opts->overlay_mode != -1)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n-\t\t    \"--no-overlay\");\n+\t\t    \"--[no]-overlay\");\n \n \tif (opts->writeout_stage)\n \t\tdie(_(\"'%s' cannot be used with switching branches\"),\n@@ -1399,7 +1399,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n@@ -1419,7 +1418,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n-\topts->overlay_mode = -1;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1593,6 +1591,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n \t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1607,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.can_switch_when_in_progress = 1;\n \topts.orphan_from_empty_tree = 0;\n \topts.empty_pathspec_ok = 1;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1645,6 +1645,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.implicit_detach = 0;\n \topts.can_switch_when_in_progress = 0;\n \topts.orphan_from_empty_tree = 1;\n+\topts.overlay_mode = -1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n@@ -1663,6 +1664,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1671,6 +1673,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n+\topts.overlay_mode = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374466","messageId":"20190425094600.15673-6-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 05/16] checkout: factor out worktree checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:49Z","receivedAt":"2019-04-25T09:46:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------\n 1 file changed, 59 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9c5abe170e..1753c8c5ed 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -330,17 +330,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,\n \t}\n }\n \n+static int checkout_worktree(const struct checkout_opts *opts)\n+{\n+\tstruct checkout state = CHECKOUT_INIT;\n+\tint nr_checkouts = 0, nr_unmerged = 0;\n+\tint errs = 0;\n+\tint pos;\n+\n+\tstate.force = 1;\n+\tstate.refresh_cache = 1;\n+\tstate.istate = &the_index;\n+\n+\tenable_delayed_checkout(&state);\n+\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\tif (ce->ce_flags & CE_MATCHED) {\n+\t\t\tif (!ce_stage(ce)) {\n+\t\t\t\terrs |= checkout_entry(ce, &state,\n+\t\t\t\t\t\t       NULL, &nr_checkouts);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (opts->writeout_stage)\n+\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n+\t\t\t\t\t\t       ce, pos,\n+\t\t\t\t\t\t       &state,\n+\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n+\t\t\telse if (opts->merge)\n+\t\t\t\terrs |= checkout_merged(pos, &state,\n+\t\t\t\t\t\t\t&nr_unmerged);\n+\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t}\n+\t}\n+\tremove_marked_cache_entries(&the_index, 1);\n+\tremove_scheduled_dirs();\n+\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n+\n+\tif (opts->count_checkout_paths) {\n+\t\tif (nr_unmerged)\n+\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n+\t\t\t\t\t      \"Recreated %d merge conflicts\",\n+\t\t\t\t\t      nr_unmerged),\n+\t\t\t\t   nr_unmerged);\n+\t\tif (opts->source_tree)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n+\t\t\t\t\t      \"Updated %d paths from %s\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts,\n+\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n+\t\t\t\t\t\t      DEFAULT_ABBREV));\n+\t\telse if (!nr_unmerged || nr_checkouts)\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n+\t\t\t\t\t      \"Updated %d paths from the index\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts);\n+\t}\n+\n+\treturn errs;\n+}\n+\n static int checkout_paths(const struct checkout_opts *opts,\n \t\t\t  const char *revision)\n {\n \tint pos;\n-\tstruct checkout state = CHECKOUT_INIT;\n \tstatic char *ps_matched;\n \tstruct object_id rev;\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n-\tint nr_checkouts = 0, nr_unmerged = 0;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -426,53 +482,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\tstate.force = 1;\n-\tstate.refresh_cache = 1;\n-\tstate.istate = &the_index;\n-\n-\tenable_delayed_checkout(&state);\n-\tfor (pos = 0; pos < active_nr; pos++) {\n-\t\tstruct cache_entry *ce = active_cache[pos];\n-\t\tif (ce->ce_flags & CE_MATCHED) {\n-\t\t\tif (!ce_stage(ce)) {\n-\t\t\t\terrs |= checkout_entry(ce, &state,\n-\t\t\t\t\t\t       NULL, &nr_checkouts);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (opts->writeout_stage)\n-\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n-\t\t\t\t\t\t       ce, pos,\n-\t\t\t\t\t\t       &state,\n-\t\t\t\t\t\t       &nr_checkouts, opts->overlay_mode);\n-\t\t\telse if (opts->merge)\n-\t\t\t\terrs |= checkout_merged(pos, &state,\n-\t\t\t\t\t\t\t&nr_unmerged);\n-\t\t\tpos = skip_same_name(ce, pos) - 1;\n-\t\t}\n-\t}\n-\tremove_marked_cache_entries(&the_index, 1);\n-\tremove_scheduled_dirs();\n-\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n-\n-\tif (opts->count_checkout_paths) {\n-\t\tif (nr_unmerged)\n-\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n-\t\t\t\t\t      \"Recreated %d merge conflicts\",\n-\t\t\t\t\t      nr_unmerged),\n-\t\t\t\t   nr_unmerged);\n-\t\tif (opts->source_tree)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n-\t\t\t\t\t      \"Updated %d paths from %s\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts,\n-\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n-\t\t\t\t\t\t      DEFAULT_ABBREV));\n-\t\telse if (!nr_unmerged || nr_checkouts)\n-\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n-\t\t\t\t\t      \"Updated %d paths from the index\",\n-\t\t\t\t\t      nr_checkouts),\n-\t\t\t\t   nr_checkouts);\n-\t}\n+\terrs |= checkout_worktree(opts);\n \n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374467","messageId":"20190425094600.15673-7-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 06/16] restore: add --worktree and --staged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:50Z","receivedAt":"2019-04-25T09:46:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"'git checkout <tree-ish> <pathspec>' updates both index and\nworktree. But updating the index when you want to restore worktree\nfiles is non-intuitive. The index contains the data ready for the next\ncommit, and there's no indication that the user will want to commit\nthe restored versions.\n\n'git restore' therefore by default only touches worktree. The user has\nthe option to update either the index with\n\n    git restore --staged --source=<tree> <path>  (1)\n\nor update both with\n\n    git restore --staged --worktree --source=<tree> <path> (2)\n\nPS. Orignally I wanted to make worktree update default and form (1)\nwould add index update while also updating the worktree, and the user\nwould need to do \"--staged --no-worktree\" to update index only. But it\nlooks really confusing that \"--staged\" option alone updates both. So\nnow form (2) is used for both, which reads much more obvious.\n\nPPS. Yes form (1) overlaps with \"git reset <rev> <path>\". I don't know\nif we can ever turn \"git reset\" back to \"_always_ reset HEAD and\noptionally do something else\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 68 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1753c8c5ed..e855c64cfe 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -66,6 +66,8 @@ struct checkout_opts {\n \tint can_switch_when_in_progress;\n \tint orphan_from_empty_tree;\n \tint empty_pathspec_ok;\n+\tint checkout_index;\n+\tint checkout_worktree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -397,6 +399,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tint checkout_index;\n \n \ttrace2_cmd_mode(opts->patch_mode ? \"patch\" : \"path\");\n \n@@ -422,9 +425,26 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n \t\t    opts->new_branch);\n \n-\tif (opts->patch_mode)\n-\t\treturn run_add_interactive(revision, \"--patch=checkout\",\n-\t\t\t\t\t   &opts->pathspec);\n+\tif (!opts->checkout_worktree && !opts->checkout_index)\n+\t\tdie(_(\"neither '%s' or '%s' is specified\"),\n+\t\t    \"--staged\", \"--worktree\");\n+\n+\tif (!opts->checkout_worktree && !opts->from_treeish)\n+\t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n+\t\t    \"--worktree\", \"--source\");\n+\n+\tif (opts->patch_mode) {\n+\t\tconst char *patch_mode;\n+\n+\t\tif (opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=checkout\";\n+\t\telse if (opts->checkout_index && !opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse\n+\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n+\t\t\t    \"--patch\", \"--worktree\");\n+\t\treturn run_add_interactive(revision, patch_mode, &opts->pathspec);\n+\t}\n \n \trepo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(&opts->pathspec) < 0)\n@@ -482,10 +502,30 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\treturn 1;\n \n \t/* Now we are committed to check them out */\n-\terrs |= checkout_worktree(opts);\n+\tif (opts->checkout_worktree)\n+\t\terrs |= checkout_worktree(opts);\n \n-\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n-\t\tdie(_(\"unable to write new index file\"));\n+\t/*\n+\t * Allow updating the index when checking out from the index.\n+\t * This is to save new stat info.\n+\t */\n+\tif (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)\n+\t\tcheckout_index = 1;\n+\telse\n+\t\tcheckout_index = opts->checkout_index;\n+\n+\tif (checkout_index) {\n+\t\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n+\t\t\tdie(_(\"unable to write new index file\"));\n+\t} else {\n+\t\t/*\n+\t\t * NEEDSWORK: if --worktree is not specified, we\n+\t\t * should save stat info of checked out files in the\n+\t\t * index to avoid the next (potentially costly)\n+\t\t * refresh. But it's a bit tricker to do...\n+\t\t */\n+\t\trollback_lock_file(&lock_file);\n+\t}\n \n \tread_ref_full(\"HEAD\", 0, &rev, NULL);\n \thead = lookup_commit_reference_gently(the_repository, &rev, 1);\n@@ -1461,6 +1501,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n+\tif (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = 0;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = 0;\n+\t} else {\n+\t\tif (opts->checkout_index < 0)\n+\t\t\topts->checkout_index = -opts->checkout_index - 1;\n+\t\tif (opts->checkout_worktree < 0)\n+\t\t\topts->checkout_worktree = -opts->checkout_worktree - 1;\n+\t}\n+\tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n+\t\tBUG(\"these flags should be non-negative by now\");\n+\n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n@@ -1617,6 +1671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.orphan_from_empty_tree = 0;\n \topts.empty_pathspec_ok = 1;\n \topts.overlay_mode = -1;\n+\topts.checkout_index = -2;    /* default on */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1674,6 +1730,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \tstruct option restore_options[] = {\n \t\tOPT_STRING('s', \"source\", &opts.from_treeish, \"<tree-ish>\",\n \t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_BOOL('S', \"staged\", &opts.checkout_index,\n+\t\t\t   N_(\"restore the index\")),\n+\t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n+\t\t\t   N_(\"restore the working tree (default)\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1684,6 +1744,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.empty_pathspec_ok = 0;\n \topts.overlay_mode = 0;\n+\topts.checkout_index = -1;    /* default off */\n+\topts.checkout_worktree = -2; /* default on */\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374468","messageId":"20190425094600.15673-8-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 07/16] restore: reject invalid combinations with --staged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:51Z","receivedAt":"2019-04-25T09:46:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-checkout rejects plenty of invalid option combinations. Since\ngit-checkout is equivalent of either\n\n    git restore --source --staged --worktree\n\nor\n\n    git restore --worktree\n\nthat still leaves the new mode 'git restore --index' unprotected. Reject\nsome more invalid option combinations.\n\nThe other new mode 'restore --source --worktree' does not need anything\nelse.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e855c64cfe..71e2589340 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -433,6 +433,16 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"'%s' must be used when '%s' is not specified\"),\n \t\t    \"--worktree\", \"--source\");\n \n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->writeout_stage)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--ours\", \"--theirs\", \"--staged\");\n+\n+\tif (opts->checkout_index && !opts->checkout_worktree &&\n+\t    opts->merge)\n+\t\tdie(_(\"'%s' or '%s' cannot be used with %s\"),\n+\t\t    \"--merge\", \"--conflict\", \"--staged\");\n+\n \tif (opts->patch_mode) {\n \t\tconst char *patch_mode;\n \n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374469","messageId":"20190425094600.15673-9-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 08/16] restore: default to --source=HEAD when only --staged is specified","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:52Z","receivedAt":"2019-04-25T09:46:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore --staged\" without --source does not make much sense since\nby default we restore from the index.  Instead of copying the index to\nitself, set the default source to HEAD in this case, yielding behavior\nthat matches \"git reset -- <paths>\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 71e2589340..09a03f1ff8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1524,6 +1524,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t}\n \tif (opts->checkout_index < 0 || opts->checkout_worktree < 0)\n \t\tBUG(\"these flags should be non-negative by now\");\n+\t/*\n+\t * convenient shortcut: \"git restore --staged\" equals\n+\t * \"git restore --staged --source HEAD\"\n+\t */\n+\tif (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)\n+\t\topts->from_treeish = \"HEAD\";\n \n \t/*\n \t * From here on, new_branch will contain the branch to be checked out,\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374470","messageId":"20190425094600.15673-10-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 09/16] restore: replace --force with --ignore-unmerged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:53Z","receivedAt":"2019-04-25T09:46:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Use a more specific option name to express its purpose. --force may come\nback as an alias of --ignore-unmerged and possibly more. But since this\nis a destructive operation, I don't see why we need to \"force\" anything\nmore. We already don't hold back.\n\nWhen 'checkout --force' or 'restore --ignore-unmerged' is used, we may\nalso print warnings about unmerged entries being ignore. Since this is\nnot exactly warning (people tell us to do so), more informational, let\nit be suppressed if --quiet is given. This is a behavior change for\ngit-checkout.\n\nPS. The diff looks a bit iffy since --force is moved to\nadd_common_switch_branch_options() (i.e. for switching). But\ngit-checkout is also doing switching and inherits this --force.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 29 ++++++++++++++++++++---------\n 1 file changed, 20 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 09a03f1ff8..824ab65886 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -68,6 +68,8 @@ struct checkout_opts {\n \tint empty_pathspec_ok;\n \tint checkout_index;\n \tint checkout_worktree;\n+\tconst char *ignore_unmerged_opt;\n+\tint ignore_unmerged;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->new_branch_log)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-l\");\n \n-\tif (opts->force && opts->patch_mode)\n-\t\tdie(_(\"'%s' cannot be used with updating paths\"), \"-f\");\n+\tif (opts->ignore_unmerged && opts->patch_mode)\n+\t\tdie(_(\"'%s' cannot be used with updating paths\"),\n+\t\t    opts->ignore_unmerged_opt);\n \n \tif (opts->force_detach)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--detach\");\n@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->merge && opts->patch_mode)\n \t\tdie(_(\"'%s' cannot be used with %s\"), \"--merge\", \"--patch\");\n \n-\tif (opts->force && opts->merge)\n-\t\tdie(_(\"'%s' cannot be used with %s\"), \"-f\", \"-m\");\n+\tif (opts->ignore_unmerged && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with %s\"),\n+\t\t    opts->ignore_unmerged_opt, \"-m\");\n \n \tif (opts->new_branch)\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n@@ -495,8 +499,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tif (ce->ce_flags & CE_MATCHED) {\n \t\t\tif (!ce_stage(ce))\n \t\t\t\tcontinue;\n-\t\t\tif (opts->force) {\n-\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n+\t\t\tif (opts->ignore_unmerged) {\n+\t\t\t\tif (!opts->quiet)\n+\t\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n \t\t\t} else if (opts->writeout_stage) {\n \t\t\t\terrs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);\n \t\t\t} else if (opts->merge) {\n@@ -1414,8 +1419,6 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n@@ -1433,6 +1436,8 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n@@ -1502,8 +1507,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n-\tif (opts->force)\n+\tif (opts->force) {\n \t\topts->discard_changes = 1;\n+\t\topts->ignore_unmerged_opt = \"--force\";\n+\t\topts->ignore_unmerged = 1;\n+\t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1750,6 +1758,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"restore the index\")),\n \t\tOPT_BOOL('W', \"worktree\", &opts.checkout_worktree,\n \t\t\t   N_(\"restore the working tree (default)\")),\n+\t\tOPT_BOOL(0, \"ignore-unmerged\", &opts.ignore_unmerged,\n+\t\t\t N_(\"ignore unmerged entries\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode\")),\n \t\tOPT_END()\n \t};\n@@ -1762,6 +1772,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \topts.overlay_mode = 0;\n \topts.checkout_index = -1;    /* default off */\n \topts.checkout_worktree = -2; /* default on */\n+\topts.ignore_unmerged_opt = \"--ignore-unmerged\";\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374471","messageId":"20190425094600.15673-11-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 10/16] restore: support --patch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:54Z","receivedAt":"2019-04-25T09:47:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-restore is different from git-checkout that it only restores the\nworktree by default, not both worktree and index. add--interactive\nneeds some update to support this mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c        |  6 +++--\n git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 56 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 824ab65886..bed79ae595 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -454,9 +454,11 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\tpatch_mode = \"--patch=checkout\";\n \t\telse if (opts->checkout_index && !opts->checkout_worktree)\n \t\t\tpatch_mode = \"--patch=reset\";\n+\t\telse if (!opts->checkout_index && opts->checkout_worktree)\n+\t\t\tpatch_mode = \"--patch=worktree\";\n \t\telse\n-\t\t\tdie(_(\"'%s' with only '%s' is not currently supported\"),\n-\t\t\t    \"--patch\", \"--worktree\");\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(revision, patch_mode, &opts->pathspec);\n \t}\n \ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..3dfb3629c9 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -149,6 +149,20 @@ sub colored {\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@@ -1049,6 +1063,12 @@ sub color_diff {\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@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {\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@@ -1421,6 +1453,16 @@ sub display_hunks {\n \t\tdeletion => N__(\"Apply deletion 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\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\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n );\n \n sub patch_update_file {\n@@ -1756,6 +1798,16 @@ sub process_args {\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-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374472","messageId":"20190425094600.15673-12-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 11/16] t: add tests for restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:55Z","receivedAt":"2019-04-25T09:47:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/lib-patch-mode.sh               |  12 ++++\n t/t2070-restore.sh (new +x)       |  99 +++++++++++++++++++++++++++\n t/t2071-restore-patch.sh (new +x) | 110 ++++++++++++++++++++++++++++++\n 3 files changed, 221 insertions(+)\n\ndiff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh\nindex 06c3c91762..cfd76bf987 100644\n--- a/t/lib-patch-mode.sh\n+++ b/t/lib-patch-mode.sh\n@@ -2,28 +2,40 @@\n \n . ./test-lib.sh\n \n+# set_state <path> <worktree-content> <index-content>\n+#\n+# Prepare the content for path in worktree and the index as specified.\n set_state () {\n \techo \"$3\" > \"$1\" &&\n \tgit add \"$1\" &&\n \techo \"$2\" > \"$1\"\n }\n \n+# save_state <path>\n+#\n+# Save index/worktree content of <path> in the files _worktree_<path>\n+# and _index_<path>\n save_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tcat \"$1\" > _worktree_\"$noslash\" &&\n \tgit show :\"$1\" > _index_\"$noslash\"\n }\n \n+# set_and_save_state <path> <worktree-content> <index-content>\n set_and_save_state () {\n \tset_state \"$@\" &&\n \tsave_state \"$1\"\n }\n \n+# verify_state <path> <expected-worktree-content> <expected-index-content>\n verify_state () {\n \ttest \"$(cat \"$1\")\" = \"$2\" &&\n \ttest \"$(git show :\"$1\")\" = \"$3\"\n }\n \n+# verify_saved_state <path>\n+#\n+# Call verify_state with expected contents from the last save_state\n verify_saved_state () {\n \tnoslash=\"$(echo \"$1\" | tr / _)\" &&\n \tverify_state \"$1\" \"$(cat _worktree_\"$noslash\")\" \"$(cat _index_\"$noslash\")\"\ndiff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\nnew file mode 100755\nindex 0000000000..73ea13ede9\n--- /dev/null\n+++ b/t/t2070-restore.sh\n@@ -0,0 +1,99 @@\n+#!/bin/sh\n+\n+test_description='restore basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\techo first-and-a-half >>first.t &&\n+\tgit add first.t &&\n+\ttest_commit second &&\n+\techo one >one &&\n+\techo two >two &&\n+\techo untracked >untracked &&\n+\techo ignored >ignored &&\n+\techo /ignored >.gitignore &&\n+\tgit add one two .gitignore &&\n+\tgit update-ref refs/heads/one master\n+'\n+\n+test_expect_success 'restore without pathspec is not ok' '\n+\ttest_must_fail git restore &&\n+\ttest_must_fail git restore --source=first\n+'\n+\n+test_expect_success 'restore a file, ignoring branch of same name' '\n+\tcat one >expected &&\n+\techo dirty >>one &&\n+\tgit restore one &&\n+\ttest_cmp expected one\n+'\n+\n+test_expect_success 'restore a file on worktree from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first first.t &&\n+\ttest_cmp expected first.t &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'restore a file in the index from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --staged first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file blob HEAD:./first.t >expected &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore a file in both the index and worktree from another ref' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob first:./first.t >expected &&\n+\tgit restore --source=first --staged --worktree first.t &&\n+\tgit show :first.t >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_cmp expected first.t\n+'\n+\n+test_expect_success 'restore --staged uses HEAD as source' '\n+\ttest_when_finished git reset --hard &&\n+\tgit cat-file blob :./first.t >expected &&\n+\techo index-dirty >>first.t &&\n+\tgit add first.t &&\n+\tgit restore --staged first.t &&\n+\tgit cat-file blob :./first.t >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '\n+\tgit init unmerged &&\n+\t(\n+\t\tcd unmerged &&\n+\t\techo one >unmerged &&\n+\t\techo one >common &&\n+\t\tgit add unmerged common &&\n+\t\tgit commit -m common &&\n+\t\tgit switch -c first &&\n+\t\techo first >unmerged &&\n+\t\tgit commit -am first &&\n+\t\tgit switch -c second master &&\n+\t\techo second >unmerged &&\n+\t\tgit commit -am second &&\n+\t\ttest_must_fail git merge first &&\n+\n+\t\techo dirty >>common &&\n+\t\ttest_must_fail git restore . &&\n+\n+\t\tgit restore --ignore-unmerged --quiet . >output 2>&1 &&\n+\t\tgit diff common >diff-output &&\n+\t\t: >empty &&\n+\t\ttest_cmp empty output &&\n+\t\ttest_cmp empty diff-output\n+\t)\n+'\n+\n+test_done\ndiff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh\nnew file mode 100755\nindex 0000000000..98b2476e7c\n--- /dev/null\n+++ b/t/t2071-restore-patch.sh\n@@ -0,0 +1,110 @@\n+#!/bin/sh\n+\n+test_description='git restore --patch'\n+\n+. ./lib-patch-mode.sh\n+\n+test_expect_success PERL 'setup' '\n+\tmkdir dir &&\n+\techo parent >dir/foo &&\n+\techo dummy >bar &&\n+\tgit add bar dir/foo &&\n+\tgit commit -m initial &&\n+\ttest_tick &&\n+\ttest_commit second dir/foo head &&\n+\tset_and_save_state bar bar_work bar_index &&\n+\tsave_head\n+'\n+\n+test_expect_success PERL 'restore -p without pathspec is fine' '\n+\techo q >cmd &&\n+\tgit restore -p <cmd\n+'\n+\n+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'\n+\n+test_expect_success PERL 'saying \"n\" does nothing' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n n | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_saved_state dir/foo\n+'\n+\n+test_expect_success PERL 'git restore -p' '\n+\tset_and_save_state dir/foo work head &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'git restore -p with staged changes' '\n+\tset_state dir/foo work index &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head index\n+'\n+\n+test_expect_success PERL 'git restore -p --source=HEAD^' '\n+\tset_state dir/foo work index &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines n y n | git restore -p --source=HEAD^ &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent index\n+'\n+\n+test_expect_success PERL 'git restore -p handles deletion' '\n+\tset_state dir/foo work index &&\n+\trm dir/foo &&\n+\ttest_write_lines n y | git restore -p &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo index index\n+'\n+\n+# The idea in the rest is that bar sorts first, so we always say 'y'\n+# first and if the path limiter fails it'll apply to bar instead of\n+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in\n+# the failure case (and thus get out of the loop).\n+\n+test_expect_success PERL 'path limiting works: dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: -- dir' '\n+\tset_state dir/foo work head &&\n+\ttest_write_lines y n | git restore -p -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | git restore -p --source=HEAD^ -- dir &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo parent head\n+'\n+\n+test_expect_success PERL 'path limiting works: foo inside dir' '\n+\tset_state dir/foo work head &&\n+\t# the third n is to get out in case it mistakenly does not apply\n+\ttest_write_lines y n n | (cd dir && git restore -p foo) &&\n+\tverify_saved_state bar &&\n+\tverify_state dir/foo head head\n+'\n+\n+test_expect_success PERL 'none of this moved HEAD' '\n+\tverify_saved_head\n+'\n+\n+test_done\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374473","messageId":"20190425094600.15673-13-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 12/16] completion: support restore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:56Z","receivedAt":"2019-04-25T09:47:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion for restore is straightforward. We could still do better\nthough by giving the list of just tracked files instead of all present\nones. But let's leave it for later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b24bc48276..58d18d41a2 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2491,6 +2491,21 @@ _git_reset ()\n \t__git_complete_refs\n }\n \n+_git_restore ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--source=*)\n+\t\t__git_complete_refs --cur=\"${cur##--source=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin restore\n+\t\t;;\n+\tesac\n+}\n+\n __git_revert_inprogress_options=\"--continue --quit --abort\"\n \n _git_revert ()\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374474","messageId":"20190425094600.15673-14-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:57Z","receivedAt":"2019-04-25T09:47:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since the operation in progress is merge, stick to the 'git merge'\nvariant of aborting. 'git reset --hard' does not really tell you about\naborting the merge by just looking, longer to type, and even though I\nknow by heart what --hard do, I still dislike it when I need to consider\nwhether --hard, --mixed or --soft.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/user-manual.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 94799faa2b..4e210970e1 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess\n away, you can always return to the pre-merge state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git merge --abort\n -------------------------------------------------\n \n Or, if you've already committed the merge that you want to throw away,\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374475","messageId":"20190425094600.15673-15-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 14/16] doc: promote \"git restore\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:58Z","receivedAt":"2019-04-25T09:47:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git restore\" (together with \"git switch\") are added\nto avoid the confusion of one-command-do-all \"git checkout\" for new\nusers. They are also helpful to avoid ambiguous context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands.\n\nOne nice thing about git-restore is the ability to restore\n\"everything\", so it can be used in \"git status\" advice instead of both\n\"git checkout\" and \"git reset\".  The three commands suggested by \"git\nstatus\" are add, rm and restore.\n\n\"git checkout\" is also removed from \"git help\" (i.e. it's no longer\nconsidered a commonly used command)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clean.txt        |  2 +-\n Documentation/git-commit.txt       |  2 +-\n Documentation/git-format-patch.txt |  2 +-\n Documentation/git-reset.txt        |  6 +--\n Documentation/git-revert.txt       |  4 +-\n Documentation/gitcli.txt           |  4 +-\n Documentation/giteveryday.txt      |  5 +-\n Documentation/gittutorial-2.txt    |  4 +-\n Documentation/gittutorial.txt      |  2 +-\n Documentation/user-manual.txt      | 12 ++---\n builtin/clone.c                    |  2 +-\n builtin/commit.c                   |  2 +-\n command-list.txt                   |  2 +-\n t/t7508-status.sh                  | 82 +++++++++++++++---------------\n t/t7512-status-help.sh             | 20 ++++----\n wt-status.c                        | 26 +++++++---\n 16 files changed, 93 insertions(+), 84 deletions(-)\n\ndiff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt\nindex 03056dad0d..8a31ccffea 100644\n--- a/Documentation/git-clean.txt\n+++ b/Documentation/git-clean.txt\n@@ -64,7 +64,7 @@ OPTIONS\n \tdirectory) and $GIT_DIR/info/exclude, but do still use the ignore\n \trules given with `-e` options.  This allows removing all untracked\n \tfiles, including build products.  This can be used (possibly in\n-\tconjunction with 'git reset') to create a pristine\n+\tconjunction with 'git restore' or 'git reset') to create a pristine\n \tworking directory to test a clean build.\n \n -X::\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex a85c2c2a4c..7628193284 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in\n your working tree are temporarily stored to a staging area\n called the \"index\" with 'git add'.  A file can be\n reverted back, only in the index but not in the working tree,\n-to that of the last commit with `git reset HEAD -- <file>`,\n+to that of the last commit with `git restore --staged <file>`,\n which effectively reverts 'git add' and prevents the changes to\n this file from participating in the next commit.  After building\n the state to be committed incrementally with these commands,\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 0a24a5679e..01bfcecf69 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:\n \n     $ git fetch <project> master:test-apply\n     $ git switch test-apply\n-    $ git reset --hard\n+    $ git restore --source=HEAD --staged --worktree :/\n     $ git am a.patch\n \n If it does not apply correctly, there can be various reasons.\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex c25f8a95b9..633d71d36a 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -29,9 +29,9 @@ This means that `git reset <paths>` is the opposite of `git add\n `git restore [--source=<tree-ish>] --staged <paths>...`.\n +\n After running `git reset <paths>` to update the index entry, you can\n-use linkgit:git-checkout[1] to check the contents out of the index to\n-the working tree.\n-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you\n+use linkgit:git-restore[1] to check the contents out of the index to\n+the working tree. Alternatively, using linkgit:git-restore[1]\n+and specifying a commit with `--source`, you\n can copy the contents of a path out of a commit to the index and to the\n working tree in one go.\n \ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 018ecf49d3..9aadc36881 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to\n throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n-should see linkgit:git-checkout[1], specifically the `git checkout\n-<commit> -- <filename>` syntax.  Take care with these alternatives as\n+should see linkgit:git-restore[1], specifically the `--source`\n+option. Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \n See \"Reset, restore and revert\" in linkgit:git[1] for the differences\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 225513ef5e..1ed3ca33b7 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n    things:\n +\n --------------------------------\n-$ git checkout -- *.c\n-$ git checkout -- \\*.c\n+$ git restore *.c\n+$ git restore \\*.c\n --------------------------------\n +\n The former lets your shell expand the fileglob, and you are asking\ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex ad455f3e39..1bd919f92b 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -51,8 +51,7 @@ following commands.\n \n   * linkgit:git-commit[1] to advance the current branch.\n \n-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with\n-    pathname parameters) to undo changes.\n+  * linkgit:git-restore[1] to undo changes.\n \n   * linkgit:git-merge[1] to merge between local branches.\n \n@@ -82,7 +81,7 @@ Create a topic branch and develop.::\n ------------\n $ git switch -c alsa-audio <1>\n $ edit/compile/test\n-$ git checkout -- curses/ux_audio_oss.c <2>\n+$ git restore curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n $ edit/compile/test\n $ git diff HEAD <4>\ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e0976f6017..8bdb7d0bd3 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -370,13 +370,13 @@ situation:\n $ git status\n On branch master\n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   closing.txt\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   file.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex e6ad6b5f8d..59ef5cef1f 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -110,7 +110,7 @@ $ git status\n On branch master\n Changes to be committed:\n Your branch is up to date with 'origin/master'.\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   file1\n \tmodified:   file2\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex 4e210970e1..8bce75b2cf 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed\n state with\n \n -------------------------------------------------\n-$ git reset --hard HEAD\n+$ git restore --staged --worktree :/\n -------------------------------------------------\n \n If you make a commit that you later wish you hadn't, there are two\n@@ -1523,12 +1523,10 @@ Checking out an old version of a file\n \n In the process of undoing a previous bad change, you may find it\n useful to check out an older version of a particular file using\n-linkgit:git-checkout[1].  We've used `git checkout` before to switch\n-branches, but it has quite different behavior if it is given a path\n-name: the command\n+linkgit:git-restore[1]. The command\n \n -------------------------------------------------\n-$ git checkout HEAD^ path/to/file\n+$ git restore --source=HEAD^ path/to/file\n -------------------------------------------------\n \n replaces path/to/file by the contents it had in the commit HEAD^, and\n@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.\n The Workflow\n ------------\n \n-High-level operations such as linkgit:git-commit[1],\n-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data\n+High-level operations such as linkgit:git-commit[1] and\n+linkgit:git-restore[1] work by moving data\n between the working tree, the index, and the object database.  Git\n provides low-level operations which perform each of these steps\n individually.\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 50bde99618..024eb57996 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -492,7 +492,7 @@ static enum {\n static const char junk_leave_repo_msg[] =\n N_(\"Clone succeeded, but checkout failed.\\n\"\n    \"You can inspect what was checked out with 'git status'\\n\"\n-   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n+   \"and retry with 'git restore --source=HEAD :/'\\n\");\n \n static void remove_junk(void)\n {\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..fa5982cc86 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n-\t\t      \"not exceeded, and then \\\"git reset HEAD\\\" to recover.\"));\n+\t\t      \"not exceeded, and then \\\"git restore --staged :/\\\" to recover.\"));\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))\n \t\twrite_commit_graph_reachable(get_object_directory(), 0, 0);\ndiff --git a/command-list.txt b/command-list.txt\nindex b9eae1c258..cf8dccb439 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n git-check-ignore                        purehelpers\n git-check-mailmap                       purehelpers\n-git-checkout                            mainporcelain           history\n+git-checkout                            mainporcelain\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n git-cherry                              plumbinginterrogators          complete\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex e1f11293e2..681bc314b4 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -94,13 +94,13 @@ test_expect_success 'status --column' '\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -128,13 +128,13 @@ cat >expect <<\\EOF\n #   (use \"git pull\" to merge the remote branch into yours)\n #\n # Changes to be committed:\n-#   (use \"git reset HEAD <file>...\" to unstage)\n+#   (use \"git restore --staged <file>...\" to unstage)\n #\n #\tnew file:   dir2/added\n #\n # Changes not staged for commit:\n #   (use \"git add <file>...\" to update what will be committed)\n-#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#   (use \"git restore <file>...\" to discard changes in working directory)\n #\n #\tmodified:   dir1/modified\n #\n@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   ../dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   modified\n \n@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \t<GREEN>new file:   dir2/added<RESET>\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \t<RED>modified:   dir1/modified<RESET>\n \n@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   dir1/modified\n \n@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD^1 <file>...\" to unstage)\n+  (use \"git restore --source=HEAD^1 --staged <file>...\" to unstage)\n \n \tnew file:   dir2/added\n \tnew file:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n   (commit or discard the untracked or modified content in submodules)\n \n \tmodified:   dir1/modified\n@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \tmodified:   sm (new commits)\n@@ -1379,13 +1379,13 @@ cat > expect << EOF\n ;   (use \"git pull\" to merge the remote branch into yours)\n ;\n ; Changes to be committed:\n-;   (use \"git reset HEAD <file>...\" to unstage)\n+;   (use \"git restore --staged <file>...\" to unstage)\n ;\n ;\tmodified:   sm\n ;\n ; Changes not staged for commit:\n ;   (use \"git add <file>...\" to update what will be committed)\n-;   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+;   (use \"git restore <file>...\" to discard changes in working directory)\n ;\n ;\tmodified:   dir1/modified\n ;\tmodified:   sm (new commits)\n@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \n@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.\n   (use \"git pull\" to merge the remote branch into yours)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   sm\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   dir1/modified\n \ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 458608cc1e..1b9712c675 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -85,7 +85,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -110,7 +110,7 @@ You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -148,7 +148,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (use \"git rebase --abort\" to check out the original branch)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   main.txt\n@@ -176,7 +176,7 @@ You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO\n   (all conflicts fixed: run \"git rebase --continue\")\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   main.txt\n \n@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\\''split_commit'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\\''several_edits'\\''\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore <file>...\" to discard changes in working directory)\n \n \tmodified:   main.txt\n \n@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Unmerged paths:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n   (use \"git add <file>...\" to mark resolution)\n \n \tboth modified:   to-revert.txt\n@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.\n   (use \"git revert --abort\" to cancel the revert operation)\n \n Changes to be committed:\n-  (use \"git reset HEAD <file>...\" to unstage)\n+  (use \"git restore --staged <file>...\" to unstage)\n \n \tmodified:   to-revert.txt\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..19fd1add75 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t;\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \n \tif (!both_deleted) {\n@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)\n \t\treturn;\n \tif (s->whence != FROM_COMMIT)\n \t\t; /* NEEDSWORK: use \"git reset --unresolve\"??? */\n-\telse if (!s->is_initial)\n-\t\tstatus_printf_ln(s, c, _(\"  (use \\\"git reset %s <file>...\\\" to unstage)\"), s->reference);\n-\telse\n+\telse if (!s->is_initial) {\n+\t\tif (!strcmp(s->reference, \"HEAD\"))\n+\t\t\tstatus_printf_ln(s, c\n+\t\t\t\t\t , _(\"  (use \\\"git restore --staged <file>...\\\" to unstage)\"));\n+\t\telse\n+\t\t\tstatus_printf_ln(s, c,\n+\t\t\t\t\t _(\"  (use \\\"git restore --source=%s --staged <file>...\\\" to unstage)\"),\n+\t\t\t\t\t s->reference);\n+\t} else\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git rm --cached <file>...\\\" to unstage)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n \telse\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n-\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n+\tstatus_printf_ln(s, c, _(\"  (use \\\"git restore <file>...\\\" to discard changes in working directory)\"));\n \tif (has_dirty_submodules)\n \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374476","messageId":"20190425094600.15673-16-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 15/16] help: move git-diff and git-reset to different groups","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:45:59Z","receivedAt":"2019-04-25T09:47:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The third column in command-list.txt determines what group a common\ncommand is printed in 'git help'.\n\n\"git reset\" is currently in the \"work on the current change (see also:\ngit help everyday)\" group. While it's true that \"git reset\" can\nmanipulate the index and can be in this group, its unique\nfunctionality is resetting HEAD, which should be the \"grow, mark,\ntweak history\" group.\n\nMoving it there will also avoid the confusion because both 'restore'\nand 'reset' are in the same group, next to each other.\n\nWhile looking at the 'group, mark, tweak history', I realize \"git\ndiff\" should not be there. All the commands in this group is about\n_changing_ the commit history while \"git diff\" is a read-only\noperation. It fits better in the \"examine the history and state\" group\n(especially when \"git status\", its close friend, is already there).\n\nThis is what we have after the reorganization:\n\n    work on the current change (see also: git help everyday)\n       add       Add file contents to the index\n       mv        Move or rename a file, a directory, or a symlink\n       restore   Restore working tree files\n       rm        Remove files from the working tree and from the index\n\n    examine the history and state (see also: git help revisions)\n       bisect    Use binary search to find the commit that introduced a bug\n       diff      Show changes between commits, commit and working tree, etc\n       grep      Print lines matching a pattern\n       log       Show commit logs\n       show      Show various types of objects\n       status    Show the working tree status\n\n    grow, mark and tweak your common history\n       branch    List, create, or delete branches\n       commit    Record changes to the repository\n       merge     Join two or more development histories together\n       rebase    Reapply commits on top of another base tip\n       reset     Reset current HEAD to the specified state\n       switch    Switch branches\n       tag       Create, list, delete or verify a tag object signed with GPG\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex cf8dccb439..a9ac72bef4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface\n git-cvsserver                           foreignscminterface\n git-daemon                              synchingrepositories\n git-describe                            mainporcelain\n-git-diff                                mainporcelain           history\n+git-diff                                mainporcelain           info\n git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff-tree                           plumbinginterrogators\n@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n-git-reset                               mainporcelain           worktree\n+git-reset                               mainporcelain           history\n git-restore                             mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374477","messageId":"20190425094600.15673-17-pclouds@gmail.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"[PATCH v3 16/16] Declare both git-switch and git-restore experimental","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T09:46:00Z","receivedAt":"2019-04-25T09:47:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These two commands are basically redesigned git-checkout. We will not\nhave that many opportunities to redo (because we run out of verbs, and\nthat would also increase maintenance cost).\n\nTo play it safe, let's declare the two commands experimental in one or\ntwo releases. If there is a serious flaw in the UI, we could still fix\nit. If everything goes well and nobody complains loudly, we can remove\nthe experimental status by reverting this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-restore.txt | 2 ++\n Documentation/git-switch.txt  | 2 ++\n 2 files changed, 4 insertions(+)\n\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex b608f3f360..d90093f195 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -28,6 +28,8 @@ commit as the restore source.\n See \"Reset, restore and revert\" in linkgit:git[1] for the differences\n between the three commands.\n \n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n OPTIONS\n -------\n -s <tree>::\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex f15e5bdcf4..197900363b 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -29,6 +29,8 @@ Switching branches does not require a clean index and working tree\n however if the operation leads to loss of local changes, unless told\n otherwise with `--discard-changes` or `--merge`.\n \n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n OPTIONS\n -------\n <branch>::\n-- \n2.21.0.854.ge34a79f761\n\n"},{"id":"374976","messageId":"20190507022127.GA220818@google.com","threadId":"50690","inReplyTo":"20190425094600.15673-1-pclouds@gmail.com","subject":"Re: [PATCH v3 00/16] Add new command 'restore'","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-05-07T02:21:27Z","receivedAt":"2019-05-07T02:21:36Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Hiya,\n\nOn Thu, Apr 25, 2019 at 04:45:44PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> v3 changes are really small\n> \n> - gitcli.txt is updated to mention the --staged/--worktree pair, in\n>   comparison to --cached/--index which is also mention there.\n> \n> - The patch 'rm --staged' is dropped. It could come back. But if it\n>   does, it should be in a series where commands that take\n>   --cached/--index will now take both --staged/--worktree. Those are\n>   'rm', 'apply', 'check-attr', 'grep', 'diff' and maybe 'ls-files'.\n>   \n>   In other words we support --staged/--worktree everywhere while\n>   --cached/--index still remains because of muscle memory. This is\n>   of course only a good move if --staged/--worktree is much better\n>   than --cached/--index.\n> \n> - Since there's a chance this series may end up in 'master' and get\n>   released, and I'm still worried that I screwed up somewhere, the last\n>   patch declares the two commands experimental for maybe one or two\n>   release cycles.\n>   \n>   If the reception is good, we revert that patch. If something\n>   horrible is found, we can still change the default behavior without\n>   anybody yelling at us. Or worst case scenario, we remove both\n>   commands and declare a failed experiment.\n> \n> PS. the intent-to-add support is still not in. But it should be in\n> before the experimental status is removed.\n\nI've got a usability comment, as we're giving restore a try within\nGoogle for now. I found myself in a situation where I had accidentally\nstaged all my changes to tracked files (I think resulting from a stash\npop which generated a merge conflict?) and didn't see a good way to\nunstage everything using restore.\n\nI tried out `git restore --staged *` and it tried to restore every build\nartifact in my working tree, all of which should be ignored, made a lot of\nnoisy errors, and left me with my changes still staged.\n\nThen, figuring that I only had a few files, I thought I'd type them\neach, with the exception of a .c/.h pair:\n\ng restore --staged builtin/log.c builtin/walken.c revision.*\n\nBecause I had build artifacts present, this vomited while trying to\nunstage revision.o, and again left me with all my changes still staged.\nrevision.o is validly ignored:\n\n$ g check-ignore revision.o                               \nrevision.o\n\nFinally explicitly naming each file which I needed to restore worked,\nbut I have very little interest in doing this for more than a handful of\nfiles, especially since the output of `git status` is not easy to paste\ninto the command line (due to the \"modified:\" etc marker).\n\nI was definitely still able to make it do what I wanted with `git reset\nHEAD` but thought you may be interested. It'd be cool to have a hint\nwhich advises how you can unstage everything, or else to pay attention\nto the gitignore and not try to unstage those files, or else to have a\ncommand to unstage everything. (I looked for one by trying `git restore\n--staged -a` but that doesn't exist :) )\n\nThanks!\nEmily\n"},{"id":"374987","messageId":"xmqqd0kun9nw.fsf@gitster-ct.c.googlers.com","threadId":"50690","inReplyTo":"20190507022127.GA220818@google.com","subject":"Re: [PATCH v3 00/16] Add new command 'restore'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-05-07T04:57:55Z","receivedAt":"2019-05-07T04:58:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> Then, figuring that I only had a few files, I thought I'd type them\n> each, with the exception of a .c/.h pair:\n>\n> g restore --staged builtin/log.c builtin/walken.c revision.*\n>\n> Because I had build artifacts present, this vomited while trying to\n> unstage revision.o, and again left me with all my changes still staged.\n> revision.o is validly ignored:\n>\n> $ g check-ignore revision.o                               \n> revision.o\n\nOften a claim \"validly ignored\" by users needs to be taken with a\ngrain of salt.  When it is in the index, it is no longer ignored.\n\nAssuming that you do not have revision.o in the index, and if\n\"restore\" is still \"checkout -- <pathspec>\" in disguise, then I\nthink you could ask \"git\" not your shell to expand the asterisk, by\nprotecting it from the shell, i.e.\n\n\t$ git restore --staged revision.\\*\n\nand the pathspec should not match untracked revision.o.  And if that\ndoes not work, you found a bug in restore, I think ;-).\n"},{"id":"375010","messageId":"CACsJy8DSVJuwNWfEcA1pv1vdoCn=EKTmhBncRtKLZedQiEj0AA@mail.gmail.com","threadId":"50690","inReplyTo":"20190507022127.GA220818@google.com","subject":"Re: [PATCH v3 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-05-07T10:36:56Z","receivedAt":"2019-05-07T10:37:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, May 7, 2019 at 9:21 AM Emily Shaffer <emilyshaffer@google.com> wrote:\n> I've got a usability comment, as we're giving restore a try within\n> Google for now.\n\nThanks. I thought I was the only guinea pig :D and obviously not a\ngood one since I know too much (which is not a good thing as a\ntester).\n\n> I found myself in a situation where I had accidentally\n> staged all my changes to tracked files (I think resulting from a stash\n> pop which generated a merge conflict?) and didn't see a good way to\n> unstage everything using restore.\n>\n> I tried out `git restore --staged *` and it tried to restore every build\n> artifact in my working tree, all of which should be ignored, made a lot of\n> noisy errors, and left me with my changes still staged.\n\nFor the record, \"git restore --staged :/\" should do the trick and it\nis documented as an example (but without --staged).\n\nEither way. I think you raise a good point about \"*\" (or patterns\nmatching more than expected in general). I need to sleep on it and see\nif the old way of handling pattern matching failure is still a good\nway to go.\n\n> Finally explicitly naming each file which I needed to restore worked,\n> but I have very little interest in doing this for more than a handful of\n> files, especially since the output of `git status` is not easy to paste\n> into the command line (due to the \"modified:\" etc marker).\n\nFor just a couple files, you could also use \"-p\" to go through them\nand either \"a\" to restore all or \"d\" to skip the file. Maybe adding\n\"-i/--interactive\" to git-restore too, just like \"git add\". Hmm...\n-- \nDuyh\n"},{"id":"375047","messageId":"20190507183104.GB220818@google.com","threadId":"50690","inReplyTo":"CACsJy8DSVJuwNWfEcA1pv1vdoCn=EKTmhBncRtKLZedQiEj0AA@mail.gmail.com","subject":"Re: [PATCH v3 00/16] Add new command 'restore'","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-05-07T18:31:04Z","receivedAt":"2019-05-07T18:31:13Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Tue, May 07, 2019 at 05:36:56PM +0700, Duy Nguyen wrote:\n> On Tue, May 7, 2019 at 9:21 AM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > I've got a usability comment, as we're giving restore a try within\n> > Google for now.\n> \n> Thanks. I thought I was the only guinea pig :D and obviously not a\n> good one since I know too much (which is not a good thing as a\n> tester).\n> \n> > I found myself in a situation where I had accidentally\n> > staged all my changes to tracked files (I think resulting from a stash\n> > pop which generated a merge conflict?) and didn't see a good way to\n> > unstage everything using restore.\n> >\n> > I tried out `git restore --staged *` and it tried to restore every build\n> > artifact in my working tree, all of which should be ignored, made a lot of\n> > noisy errors, and left me with my changes still staged.\n> \n> For the record, \"git restore --staged :/\" should do the trick and it\n> is documented as an example (but without --staged).\n\nYeah, this worked, and today I also noted `git restore --staged .`\nworks, as does Junio's suggestion on the other mail (`git restore\n--staged revision.\\*`), and quoting the * (`git restore --staged '*'`).\nSo maybe I didn't think outside the box enough before mailing :)\n\n> \n> Either way. I think you raise a good point about \"*\" (or patterns\n> matching more than expected in general). I need to sleep on it and see\n> if the old way of handling pattern matching failure is still a good\n> way to go.\n\nI think it's worth considering, especially as `git reset HEAD *` and\n`git reset HEAD` both work. (`git restore --staged` complains that it\nhasn't got any paths, but reset seems to figure you just mean\neverything.) It was a surprising failure to me coming away from years of\nusing reset.\n\n> \n> > Finally explicitly naming each file which I needed to restore worked,\n> > but I have very little interest in doing this for more than a handful of\n> > files, especially since the output of `git status` is not easy to paste\n> > into the command line (due to the \"modified:\" etc marker).\n> \n> For just a couple files, you could also use \"-p\" to go through them\n> and either \"a\" to restore all or \"d\" to skip the file. Maybe adding\n> \"-i/--interactive\" to git-restore too, just like \"git add\". Hmm...\n> -- \n> Duyh\n\nAnyways, maybe it's not a particularly weighty complaint, since there\nare still ways to mass unstage, and if I had just RTFM I would have\nfound them. :) It's a new workflow, after all, and the old one still\nworks, so it's probably not reasonable to expect it all to just work the\nsame way with s/reset HEAD/restore --staged/.\n\nThanks!\n - Emily\n"},{"id":"375104","messageId":"CACsJy8Axv6WeDQaU_Gm-oX7gSHRB0QWJz=gYh+GAgfo7gUYWcQ@mail.gmail.com","threadId":"50690","inReplyTo":"20190507183104.GB220818@google.com","subject":"Re: [PATCH v3 00/16] Add new command 'restore'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-05-08T10:20:20Z","receivedAt":"2019-05-08T10:20:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, May 8, 2019 at 1:31 AM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > > I found myself in a situation where I had accidentally\n> > > staged all my changes to tracked files (I think resulting from a stash\n> > > pop which generated a merge conflict?) and didn't see a good way to\n> > > unstage everything using restore.\n> > >\n> > > I tried out `git restore --staged *` and it tried to restore every build\n> > > artifact in my working tree, all of which should be ignored, made a lot of\n> > > noisy errors, and left me with my changes still staged.\n> >\n> > For the record, \"git restore --staged :/\" should do the trick and it\n> > is documented as an example (but without --staged).\n>\n> Yeah, this worked, and today I also noted `git restore --staged .`\n> works, as does Junio's suggestion on the other mail (`git restore\n> --staged revision.\\*`), and quoting the * (`git restore --staged '*'`).\n> So maybe I didn't think outside the box enough before mailing :)\n\nNo it's good. It actually got me thinking. Actually I take that back.\nIt's bad, I'm quite stuck at thinking here :D\n\n> > Either way. I think you raise a good point about \"*\" (or patterns\n> > matching more than expected in general). I need to sleep on it and see\n> > if the old way of handling pattern matching failure is still a good\n> > way to go.\n>\n> I think it's worth considering, especially as `git reset HEAD *` and\n> `git reset HEAD` both work. (`git restore --staged` complains that it\n> hasn't got any paths, but reset seems to figure you just mean\n> everything.) It was a surprising failure to me coming away from years of\n> using reset.\n\nYeah I agree that doing it the \"git reset\" way makes sense for\n--staged because shell expansion is tied to worktree files, and\n--staged is completely disconnected from worktree. Shell expansion\ncannot get this case right, no point punishing the user because of it.\n\nMy problem is \"what about the other two cases, --worktree alone or\n--worktree with --staged (iow do we care about consistency)? what\nabout git-checkout? does the old way even make sense for git-checkout?\nany better way to do if the reason behind is still valid?\". I\nobviously haven't worked it all out yet.\n-- \nDuy\n"},{"id":"377117","messageId":"20190613151918.GB31952@szeder.dev","threadId":"50690","inReplyTo":"20190425094600.15673-12-pclouds@gmail.com","subject":"Re: [PATCH v3 11/16] t: add tests for restore","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-06-13T15:19:18Z","receivedAt":"2019-06-13T15:19:30Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Thu, Apr 25, 2019 at 04:45:55PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh\n> new file mode 100755\n> index 0000000000..73ea13ede9\n> --- /dev/null\n> +++ b/t/t2070-restore.sh\n\n> +test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '\n> +\tgit init unmerged &&\n> +\t(\n> +\t\tcd unmerged &&\n> +\t\techo one >unmerged &&\n> +\t\techo one >common &&\n> +\t\tgit add unmerged common &&\n> +\t\tgit commit -m common &&\n> +\t\tgit switch -c first &&\n> +\t\techo first >unmerged &&\n> +\t\tgit commit -am first &&\n> +\t\tgit switch -c second master &&\n> +\t\techo second >unmerged &&\n> +\t\tgit commit -am second &&\n> +\t\ttest_must_fail git merge first &&\n> +\n> +\t\techo dirty >>common &&\n> +\t\ttest_must_fail git restore . &&\n> +\n> +\t\tgit restore --ignore-unmerged --quiet . >output 2>&1 &&\n> +\t\tgit diff common >diff-output &&\n> +\t\t: >empty &&\n> +\t\ttest_cmp empty output &&\n> +\t\ttest_cmp empty diff-output\n\nPlease use 'test_must_be_empty'.\n\n(and perhaps 'git diff --exit-code common'?  dunno)\n\n"}]}