git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / Atom feed
* [PATCH v1 00/11] And new command "restore"
@ 2019-03-08 10:16 Nguyễn Thái Ngọc Duy
  2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
                   ` (12 more replies)
  0 siblings, 13 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

This is the companion of "git switch" [1] and is based on that topic.
This command peforms the "checkout paths" from git-checkout, git-reset
and also has a third mode to reset only worktree, leaving the index
alone.

For new people not aware of previous discussions, this command is
supposed to be a friendlier replacement for "git checkout" and
hopefully fixes some warts that have grown over the time in git-checkout.
For this reason, the last patch starts to recommend "git restore"
everywhere.

For old people, I'm surprised nobody reacts to the command rename from
restore-files to restore. "restore" vs "reset" is certainly confusing.
My hope was to remove "common porcelain" status from reset, but I
probably will not succeed in doing that.

Some open issues from the last discussion [2]

- intent-to-add support. This will come but maybe later.

- --index has a different meaning in git-apply. I don't have a good
  suggestion except "yeah we have two different worlds now, the old
  and the new one"

The series is also available at [3]

[1] https://public-inbox.org/git/20190308095752.8574-1-pclouds@gmail.com
[2] https://public-inbox.org/git/CACsJy8CQhWeC3b6eGPePuRejfOx7c17X61-wqq5kOiRzYkRESw@mail.gmail.com/
[3] https://gitlab.com/pclouds/git/tree/switch-and-restore

Nguyễn Thái Ngọc Duy (11):
  checkout: split part of it to new command 'restore'
  restore: take tree-ish from --source option instead
  restore: make pathspec mandatory
  restore: disable overlay mode by default
  checkout: factor out worktree checkout code
  restore: add --worktree and --index
  restore: default to --source=HEAD when only --index is specified
  restore: support --patch
  t: add tests for restore
  completion: support restore
  doc: promote "git restore"

 .gitignore                             |   1 +
 Documentation/config/interactive.txt   |   3 +-
 Documentation/git-checkout.txt         |   1 +
 Documentation/git-clean.txt            |   2 +-
 Documentation/git-commit.txt           |   4 +-
 Documentation/git-format-patch.txt     |   2 +-
 Documentation/git-reset.txt            |   6 +-
 Documentation/git-restore.txt (new)    | 172 ++++++++++++++++
 Documentation/git-revert.txt           |   4 +-
 Documentation/gitcli.txt               |   4 +-
 Documentation/giteveryday.txt          |   4 +-
 Documentation/gittutorial-2.txt        |   4 +-
 Documentation/gittutorial.txt          |   2 +-
 Documentation/user-manual.txt          |  14 +-
 Makefile                               |   1 +
 builtin.h                              |   1 +
 builtin/checkout.c                     | 259 +++++++++++++++++++------
 builtin/clone.c                        |   2 +-
 builtin/commit.c                       |   2 +-
 command-list.txt                       |   3 +-
 contrib/completion/git-completion.bash |  15 ++
 git-add--interactive.perl              |  52 +++++
 git.c                                  |   1 +
 t/lib-patch-mode.sh                    |  12 ++
 t/t2070-restore.sh (new +x)            |  77 ++++++++
 t/t2071-restore-patch.sh (new +x)      | 105 ++++++++++
 t/t7508-status.sh                      |  82 ++++----
 t/t7512-status-help.sh                 |  20 +-
 wt-status.c                            |  26 ++-
 29 files changed, 736 insertions(+), 145 deletions(-)
 create mode 100644 Documentation/git-restore.txt
 create mode 100755 t/t2070-restore.sh
 create mode 100755 t/t2071-restore-patch.sh

-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-08 18:01   ` Elijah Newren
  2019-03-13  9:17   ` Johannes Schindelin
  2019-03-08 10:16 ` [PATCH v1 02/11] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
                   ` (11 subsequent siblings)
  12 siblings, 2 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

Previously the switching branch business of 'git checkout' becomes a
new command. This adds restore command for the checking out
paths path.

Similar to switch, a new man page is added to describe what the
command will become. The implementation will be updated shortly to
match the man page.

A couple of differences from 'git checkout' worth highlighting:

- 'restore' by default will only update worktree. This matters more
  when --source is specified ('checkout <tree> <paths>' updates both
  worktree and index).

- 'restore --index' can be used to restore the index. This command
  overlaps with 'git reset <paths>'.

- both worktree and index could also be restored at the same time
  (from a tree). This overlaps with 'git checkout <tree> <paths>'

- default source for restoring worktree and index is the index and
  HEAD respectively. A different (tree) source could be specified as
  with --source (*).

- when both index and worktree are restored, --source must be
  specified since the default source for these two individual targets
  are different (**)

- --no-overlay is enabled by default, if an entry is missing in the
  source, restoring means deleting the entry

(*) I originally went with --from instead of --source. I still think
  --from is a better name. The short option -f however is already
  taken by force. And I do think short option is good to have, e.g. to
  write -s@ or -s@^ instead of --source=HEAD.

(**) If you sit down and think about it, moving worktree's source from
  the index to HEAD makes sense, but nobody is really thinking it
  through when they type the commands.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                           |   1 +
 Documentation/config/interactive.txt |   3 +-
 Documentation/git-checkout.txt       |   1 +
 Documentation/git-restore.txt (new)  | 172 +++++++++++++++++++++++++++
 Makefile                             |   1 +
 builtin.h                            |   1 +
 builtin/checkout.c                   |  26 ++++
 command-list.txt                     |   1 +
 git.c                                |   1 +
 9 files changed, 206 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index c687b92b1c..fb377106be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@
 /git-request-pull
 /git-rerere
 /git-reset
+/git-restore
 /git-rev-list
 /git-rev-parse
 /git-revert
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index ad846dd7c9..a2d3c7ec44 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -2,7 +2,8 @@ interactive.singleKey::
 	In interactive commands, allow the user to provide one-letter
 	input with a single key (i.e., without hitting enter).
 	Currently this is used by the `--patch` mode of
-	linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+	linkgit:git-add[1], linkgit:git-checkout[1],
+	linkgit:git-restore[1], linkgit:git-commit[1],
 	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
 	setting is silently ignored if portable keystroke input
 	is not available; requires the Perl module Term::ReadKey.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 2b776c1269..e107099c8c 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -571,6 +571,7 @@ $ git add frotz
 SEE ALSO
 --------
 linkgit:git-switch[1]
+linkgit:git-restore[1]
 
 GIT
 ---
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
new file mode 100644
index 0000000000..a667a5ced4
--- /dev/null
+++ b/Documentation/git-restore.txt
@@ -0,0 +1,172 @@
+git-restore(1)
+==============
+
+NAME
+----
+git-restore - Restore working tree files
+
+SYNOPSIS
+--------
+[verse]
+'git restore' [<options>] [--source=<revision>] <pathspec>...
+'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]
+
+DESCRIPTION
+-----------
+Restore paths in the working tree by replacing with the contents in
+the restore source or remove if the paths do not exist in the restore
+source. The source is by default the index but could be from a commit.
+The command can also optionally restore content in the index from a
+commit.
+
+When a `<revision>` is given, the paths that match the `<pathspec>` are
+updated both in the index and in the working tree.
+
+OPTIONS
+-------
+-s<tree>::
+--source=<tree>::
+	Restore the working tree files with the content from the given
+	tree or any revision that leads to a tree (e.g. a commit or a
+	branch).
+
+-p::
+--patch::
+	Interactively select hunks in the difference between the
+	`<revision>` (or the index, if unspecified) and the working
+	tree. See the ``Interactive Mode'' section of linkgit:git-add[1]
+	to learn how to operate the `--patch` mode.
+
+-W::
+--worktree::
+-I::
+--index::
+	Specify the restore location. If neither option is specified,
+	by default the working tree is restored. If `--index` is
+	specified without `--worktree` or `--source`, `--source=HEAD`
+	is implied. These options only make sense to use with
+	`--source`.
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless `--quiet`
+	is specified. This flag enables progress reporting even if not
+	attached to a terminal, regardless of `--quiet`.
+
+-f::
+--force::
+	If `--source` is not specified, unmerged entries are left alone
+	and will not fail the operation. Unmerged entries are always
+	replaced if `--source` is specified, regardless of `--force`.
+
+--ours::
+--theirs::
+	Check out stage #2 ('ours') or #3 ('theirs') for unmerged
+	paths.
++
+Note that during `git rebase` and `git pull --rebase`, 'ours' and
+'theirs' may appear swapped. See the explanation of the same options
+in linkgit:git-checkout[1] for details.
+
+-m::
+--merge::
+	Recreate the conflicted merge in the specified paths.
+
+--conflict=<style>::
+	The same as `--merge` option above, but changes the way the
+	conflicting hunks are presented, overriding the merge.conflictStyle
+	configuration variable.  Possible values are "merge" (default)
+	and "diff3" (in addition to what is shown by "merge" style,
+	shows the original contents).
+
+--ignore-skip-worktree-bits::
+	In sparse checkout mode, by default update only entries
+	matched by `<pathspec>` and sparse patterns in
+	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
+	patterns and unconditionally restores any files in `<pathspec>`.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach the
+	submodules HEAD.
+
+--overlay::
+--no-overlay::
+	In overlay mode, `git checkout` never removes files from the
+	index or the working tree. In no-overlay mode, files that
+	appear in the index and working tree, but not in `--source` tree
+	are removed, to make them match `<tree-ish>` exactly. The
+	default is no-overlay mode.
+
+EXAMPLES
+--------
+
+The following sequence checks out the `master` branch, reverts
+the `Makefile` to two revisions back, deletes hello.c by
+mistake, and gets it back from the index.
+
+------------
+$ git switch master
+$ git restore --source master~2 Makefile  <1>
+$ rm -f hello.c
+$ git restore hello.c                   <2>
+------------
+
+<1> take a file out of another commit
+<2> restore hello.c from the index
+
+If you want to check out _all_ C source files out of the index,
+you can say
+
+------------
+$ git restore '*.c'
+------------
+
+Note the quotes around `*.c`.  The file `hello.c` will also be
+checked out, even though it is no longer in the working tree,
+because the file globbing is used to match entries in the index
+(not in the working tree by the shell).
+
+To restore all files in the current directory
+
+------------
+$ git restore .
+------------
+
+or to restore all working tree files with 'top' pathspec magic (see
+linkgit::gitglossary[7])
+
+------------
+$ git restore :/
+------------
+
+To restore a file in the index only (this is the same as using
+"git reset")
+
+------------
+$ git restore --index hello.c
+------------
+
+or you can restore both the index and the working tree
+
+------------
+$ git restore --source=HEAD --index --worktree hello.c
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 8e91db73ad..ffe7e4f58f 100644
--- a/Makefile
+++ b/Makefile
@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
+BUILT_INS += git-restore$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
diff --git a/builtin.h b/builtin.h
index c64e44450e..6830000e14 100644
--- a/builtin.h
+++ b/builtin.h
@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_repack(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
+extern int cmd_restore(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4903359b49..11dd2ae44c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -37,6 +37,11 @@ static const char * const switch_branch_usage[] = {
 	NULL,
 };
 
+static const char * const restore_files_usage[] = {
+	N_("git restore [<options>] [<branch>] -- <file>..."),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1528,3 +1533,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	FREE_AND_NULL(options);
 	return ret;
 }
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_pathspec = 1;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, restore_files_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 13317f47d4..b9eae1c258 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           worktree
+git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
 git-rev-parse                           plumbinginterrogators
diff --git a/git.c b/git.c
index 39582cf511..6d439e723f 100644
--- a/git.c
+++ b/git.c
@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "replace", cmd_replace, RUN_SETUP },
 	{ "rerere", cmd_rerere, RUN_SETUP },
 	{ "reset", cmd_reset, RUN_SETUP },
+	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
 	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
 	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
 	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 02/11] restore: take tree-ish from --source option instead
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
  2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 18:13   ` Elijah Newren
  2019-03-10  7:58   ` Eric Sunshine
  2019-03-08 10:16 ` [PATCH v1 03/11] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
                   ` (10 subsequent siblings)
  12 siblings, 2 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

This is another departure from 'git checkout' syntax, which uses -- to
separate ref and pathspec. The observation is restore (or "git
checkout ,, <pathspec>") is most often used to restore some files from
the index. If this is correct, we can simplify it by taking a way the
ref, so that we can write

    git restore some-file

without worrying about some-file being a ref and whether we need to do

    git restore -- some-file

for safety. If the source of the restore comes from a tree, it will be
in the form of an option with value, e.g.

    git restore --source=this-tree some-file

This is of course longer to type than using "--". But hopefully it
will not be used as often, and it is clearly easier to understand.

dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()
because it's irrelevant because we don't really care about dwim-ing.
With accept_ref being unset, dwim can't happen.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 11dd2ae44c..838343d6aa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {
 };
 
 static const char * const restore_files_usage[] = {
-	N_("git restore [<options>] [<branch>] -- <file>..."),
+	N_("git restore [<options>] [--source=<branch>] <file>..."),
 	NULL,
 };
 
@@ -57,6 +57,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int dwim_new_local_branch;
+	int accept_ref;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
@@ -72,6 +73,7 @@ struct checkout_opts {
 	int branch_exists;
 	const char *prefix;
 	struct pathspec pathspec;
+	const char *from_treeish;
 	struct tree *source_tree;
 };
 
@@ -1324,6 +1326,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
+	int parseopt_flags = 0;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1335,8 +1338,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	argc = parse_options(argc, argv, prefix, options, usagestr,
-			     PARSE_OPT_KEEP_DASHDASH);
+	if (!opts->accept_pathspec && !opts->accept_ref)
+		BUG("make up your mind, you need to take _something_");
+	if (opts->accept_pathspec && opts->accept_ref)
+		parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     usagestr, parseopt_flags);
 
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
@@ -1393,7 +1401,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	 * including "last branch" syntax and DWIM-ery for names of
 	 * remote branches, erroring out for invalid or ambiguous cases.
 	 */
-	if (argc) {
+	if (argc && opts->accept_ref) {
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
@@ -1405,6 +1413,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
+	} else if (!opts->accept_ref && opts->from_treeish) {
+		struct object_id rev;
+
+		if (get_oid_mb(opts->from_treeish, &rev))
+			die(_("could not resolve %s"), opts->from_treeish);
+
+		setup_new_branch_info_and_source_tree(&new_branch_info,
+						      opts, &rev,
+						      opts->from_treeish);
+
+		if (!opts->source_tree)
+			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
 	if (argc) {
@@ -1488,6 +1508,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.only_merge_on_switching_branches = 0;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1519,6 +1540,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 0;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
@@ -1537,15 +1559,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 int cmd_restore(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option restore_options[] = {
+		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+			   N_("where the checkout from")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.dwim_new_local_branch = 1;
-	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 03/11] restore: make pathspec mandatory
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
  2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
  2019-03-08 10:16 ` [PATCH v1 02/11] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 18:35   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 04/11] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

"git restore" without arguments does not make much sense when
it's about restoring files (what files now?). We could default to
either

    git restore .

or

    git restore :/

Neither is intuitive. Make the user always give pathspec, force the
user to think the scope of restore they want because this is a
destructive operation.

"git restore -p" without pathspec is an exception to this
because it really is a separate mode. It will be treated as running
patch mode on the whole worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 838343d6aa..c52ce13d2a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -61,6 +61,7 @@ struct checkout_opts {
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
+	int empty_pathspec_ok;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1427,6 +1428,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
+	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+	    !opts->patch_mode)	/* patch mode is special */
+		die(_("pathspec is required"));
+
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
 			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1511,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.accept_ref = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
+	opts.empty_pathspec_ok = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1570,6 +1576,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
+	opts.empty_pathspec_ok = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 04/11] restore: disable overlay mode by default
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 03/11] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 18:37   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 05/11] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

Overlay mode is considered confusing when the command is about
restoring files on worktree. Disable it by default. The user can still
turn it on, or use 'git checkout' which still has overlay mode on by
default.

While at there make the check in checkout_branch() stricter. Neither
--overlay or --no-overlay should be accepted in branch switching mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index c52ce13d2a..9e59bf792f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1196,9 +1196,9 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
 
-	if (!opts->overlay_mode)
+	if (opts->overlay_mode != -1)
 		die(_("'%s' cannot be used with switching branches"),
-		    "--no-overlay");
+		    "--[no]-overlay");
 
 	if (opts->writeout_stage)
 		die(_("'%s' cannot be used with switching branches"),
@@ -1313,7 +1313,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	struct option *newopts = parse_options_concat(prevopts, options);
@@ -1333,7 +1332,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
-	opts->overlay_mode = -1;
 
 	git_config(git_checkout_config, opts);
 
@@ -1505,6 +1503,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
 			 N_("second guess 'git checkout <no-such-branch>' (default)")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	int ret;
@@ -1517,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.empty_pathspec_ok = 1;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1551,6 +1551,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
@@ -1569,6 +1570,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
 	int ret;
@@ -1577,6 +1579,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
+	opts.overlay_mode = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 05/11] checkout: factor out worktree checkout code
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 04/11] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-08 10:16 ` [PATCH v1 06/11] restore: add --worktree and --index Nguyễn Thái Ngọc Duy
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------
 1 file changed, 59 insertions(+), 49 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9e59bf792f..5fb85e7b73 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -326,17 +326,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 	}
 }
 
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+	struct checkout state = CHECKOUT_INIT;
+	int nr_checkouts = 0, nr_unmerged = 0;
+	int errs = 0;
+	int pos;
+
+	state.force = 1;
+	state.refresh_cache = 1;
+	state.istate = &the_index;
+
+	enable_delayed_checkout(&state);
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce->ce_flags & CE_MATCHED) {
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state,
+						       NULL, &nr_checkouts);
+				continue;
+			}
+			if (opts->writeout_stage)
+				errs |= checkout_stage(opts->writeout_stage,
+						       ce, pos,
+						       &state,
+						       &nr_checkouts, opts->overlay_mode);
+			else if (opts->merge)
+				errs |= checkout_merged(pos, &state,
+							&nr_unmerged);
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	remove_marked_cache_entries(&the_index, 1);
+	remove_scheduled_dirs();
+	errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+	if (opts->count_checkout_paths) {
+		if (nr_unmerged)
+			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+					      "Recreated %d merge conflicts",
+					      nr_unmerged),
+				   nr_unmerged);
+		if (opts->source_tree)
+			fprintf_ln(stderr, Q_("Updated %d path from %s",
+					      "Updated %d paths from %s",
+					      nr_checkouts),
+				   nr_checkouts,
+				   find_unique_abbrev(&opts->source_tree->object.oid,
+						      DEFAULT_ABBREV));
+		else if (!nr_unmerged || nr_checkouts)
+			fprintf_ln(stderr, Q_("Updated %d path from the index",
+					      "Updated %d paths from the index",
+					      nr_checkouts),
+				   nr_checkouts);
+	}
+
+	return errs;
+}
+
 static int checkout_paths(const struct checkout_opts *opts,
 			  const char *revision)
 {
 	int pos;
-	struct checkout state = CHECKOUT_INIT;
 	static char *ps_matched;
 	struct object_id rev;
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
-	int nr_checkouts = 0, nr_unmerged = 0;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -422,53 +478,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	state.force = 1;
-	state.refresh_cache = 1;
-	state.istate = &the_index;
-
-	enable_delayed_checkout(&state);
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		if (ce->ce_flags & CE_MATCHED) {
-			if (!ce_stage(ce)) {
-				errs |= checkout_entry(ce, &state,
-						       NULL, &nr_checkouts);
-				continue;
-			}
-			if (opts->writeout_stage)
-				errs |= checkout_stage(opts->writeout_stage,
-						       ce, pos,
-						       &state,
-						       &nr_checkouts, opts->overlay_mode);
-			else if (opts->merge)
-				errs |= checkout_merged(pos, &state,
-							&nr_unmerged);
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-	remove_marked_cache_entries(&the_index, 1);
-	remove_scheduled_dirs();
-	errs |= finish_delayed_checkout(&state, &nr_checkouts);
-
-	if (opts->count_checkout_paths) {
-		if (nr_unmerged)
-			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
-					      "Recreated %d merge conflicts",
-					      nr_unmerged),
-				   nr_unmerged);
-		if (opts->source_tree)
-			fprintf_ln(stderr, Q_("Updated %d path from %s",
-					      "Updated %d paths from %s",
-					      nr_checkouts),
-				   nr_checkouts,
-				   find_unique_abbrev(&opts->source_tree->object.oid,
-						      DEFAULT_ABBREV));
-		else if (!nr_unmerged || nr_checkouts)
-			fprintf_ln(stderr, Q_("Updated %d path from the index",
-					      "Updated %d paths from the index",
-					      nr_checkouts),
-				   nr_checkouts);
-	}
+	errs |= checkout_worktree(opts);
 
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 06/11] restore: add --worktree and --index
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 05/11] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 18:52   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified Nguyễn Thái Ngọc Duy
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

'git checkout <tree-ish> <pathspec>' updates both index and
worktree. But updating the index when you want to restore worktree
files is non-intuitive. The index contains the data ready for the next
commit, and there's no indication that the user will want to commit
the restored versions.

'git restore' therefore by default only touches worktree. The user has
the option to update either the index with

    git restore --source=<tree> --index <path>  (1)

or update both with

    git restore --source=<tree> --index --worktree <path> (2)

PS. Orignally I wanted to make worktree update default and form (1)
would add index update while also updating the worktree, and the user
would need to do "--index --no-worktree" to update index only. But it
looks really confusing that "--index" option alone updates both. So
now form (2) is used for both, which reads much more obvious.

PPS. Yes form (1) overlaps with "git reset <rev> <path>". I don't know
if we can ever turn "git reset" back to "_always_ reset HEAD and
optionally do something else".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 6 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5fb85e7b73..07b431be51 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -62,6 +62,8 @@ struct checkout_opts {
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
 	int empty_pathspec_ok;
+	int checkout_index;
+	int checkout_worktree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -393,6 +395,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
+	int checkout_index;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -418,9 +421,26 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
 		    opts->new_branch);
 
-	if (opts->patch_mode)
-		return run_add_interactive(revision, "--patch=checkout",
-					   &opts->pathspec);
+	if (!opts->checkout_worktree && !opts->checkout_index)
+		die(_("neither '%s' or '%s' is specified"),
+		    "--index", "--worktree");
+
+	if (!opts->source_tree && !opts->checkout_worktree)
+		die(_("'%s' must be used when '%s' is not specified"),
+		    "--worktree", "--source");
+
+	if (opts->patch_mode) {
+		const char *patch_mode;
+
+		if (opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=checkout";
+		else if (opts->checkout_index && !opts->checkout_worktree)
+			patch_mode = "--patch=reset";
+		else
+			die(_("'%s' with only '%s' is not currently supported"),
+			    "--patch", "--worktree");
+		return run_add_interactive(revision, patch_mode, &opts->pathspec);
+	}
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(&opts->pathspec) < 0)
@@ -478,10 +498,30 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	errs |= checkout_worktree(opts);
+	if (opts->checkout_worktree)
+		errs |= checkout_worktree(opts);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-		die(_("unable to write new index file"));
+	/*
+	 * Allow updating the index when checking out from the index.
+	 * This is to save new stat info.
+	 */
+	if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+		checkout_index = 1;
+	else
+		checkout_index = opts->checkout_index;
+
+	if (checkout_index) {
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die(_("unable to write new index file"));
+	} else {
+		/*
+		 * NEEDSWORK: if --worktree is not specified, we
+		 * should save stat info of checked out files in the
+		 * index to avoid the next (potentially costly)
+		 * refresh. But it's a bit tricker to do...
+		 */
+		rollback_lock_file(&lock_file);
+	}
 
 	read_ref_full("HEAD", 0, &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -1373,6 +1413,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
+	if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = 0;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = 0;
+	} else {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = -opts->checkout_index - 1;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = -opts->checkout_worktree - 1;
+	}
+	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+		BUG("these flags should be non-negative by now");
+
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
 	 * and new_branch_force and new_orphan_branch will tell us which one of
@@ -1527,6 +1581,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.implicit_detach = 1;
 	opts.empty_pathspec_ok = 1;
 	opts.overlay_mode = -1;
+	opts.checkout_index = -2;    /* default on */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1580,6 +1636,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL('I', "index", &opts.checkout_index,
+			   N_("restore the index")),
+		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+			   N_("restore the working tree (default)")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1590,6 +1650,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
 	opts.overlay_mode = 0;
+	opts.checkout_index = -1;    /* default off */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (5 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 06/11] restore: add --worktree and --index Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 18:58   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 08/11] restore: support --patch Nguyễn Thái Ngọc Duy
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

"git restore --index" does not make much sense since we're told to
restore the index from the (by default) index. Set default source to
HEAD in this case. This is basically the same as "git reset -- <paths>".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 07b431be51..f06439dbeb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1426,6 +1426,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	}
 	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
 		BUG("these flags should be non-negative by now");
+	/*
+	 * convenient shortcut: "git restore --index" equals
+	 * "git restore --index --source HEAD"
+	 */
+	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+		opts->from_treeish = "HEAD";
 
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 08/11] restore: support --patch
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (6 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 19:02   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

git-restore is different from git-checkout that it only restores the
worktree by default, not both worktree and index. add--interactive
needs some update to support this mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c        |  5 ++--
 git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f06439dbeb..73de110d42 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -436,9 +436,10 @@ static int checkout_paths(const struct checkout_opts *opts,
 			patch_mode = "--patch=checkout";
 		else if (opts->checkout_index && !opts->checkout_worktree)
 			patch_mode = "--patch=reset";
+		else if (!opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=worktree";
 		else
-			die(_("'%s' with only '%s' is not currently supported"),
-			    "--patch", "--worktree");
+			BUG("either flag must have been set");
 		return run_add_interactive(revision, patch_mode, &opts->pathspec);
 	}
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 20eb81cc92..3dfb3629c9 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -149,6 +149,20 @@ sub colored {
 		FILTER => undef,
 		IS_REVERSE => 0,
 	},
+	'worktree_head' => {
+		DIFF => 'diff-index -p',
+		APPLY => sub { apply_patch 'apply -R', @_ },
+		APPLY_CHECK => 'apply -R',
+		FILTER => undef,
+		IS_REVERSE => 1,
+	},
+	'worktree_nothead' => {
+		DIFF => 'diff-index -R -p',
+		APPLY => sub { apply_patch 'apply', @_ },
+		APPLY_CHECK => 'apply',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
 );
 
 $patch_mode = 'stage';
@@ -1049,6 +1063,12 @@ sub color_diff {
 marked for discarding."),
 	checkout_nothead => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+	worktree_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+	worktree_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
 marked for applying."),
 );
 
@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {
 n - do not apply this hunk to index and worktree
 q - quit; do not apply this hunk or any of the remaining ones
 a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+	worktree_head => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+	worktree_nothead => N__(
+"y - apply this hunk to worktree
+n - do not apply this hunk to worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
 d - do not apply this hunk or any of the later hunks in the file"),
 );
 
@@ -1421,6 +1453,16 @@ sub display_hunks {
 		deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
 		hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
 	},
+	worktree_head => {
+		mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+	},
+	worktree_nothead => {
+		mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+	},
 );
 
 sub patch_update_file {
@@ -1756,6 +1798,16 @@ sub process_args {
 						       'checkout_head' : 'checkout_nothead');
 					$arg = shift @ARGV or die __("missing --");
 				}
+			} elsif ($1 eq 'worktree') {
+				$arg = shift @ARGV or die __("missing --");
+				if ($arg eq '--') {
+					$patch_mode = 'checkout_index';
+				} else {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'worktree_head' : 'worktree_nothead');
+					$arg = shift @ARGV or die __("missing --");
+				}
 			} elsif ($1 eq 'stage' or $1 eq 'stash') {
 				$patch_mode = $1;
 				$arg = shift @ARGV or die __("missing --");
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 09/11] t: add tests for restore
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (7 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 08/11] restore: support --patch Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 16:53   ` Andrei Rybak
                     ` (2 more replies)
  2019-03-08 10:16 ` [PATCH v1 10/11] completion: support restore Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  12 siblings, 3 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/lib-patch-mode.sh               |  12 ++++
 t/t2070-restore.sh (new +x)       |  77 ++++++++++++++++++++++
 t/t2071-restore-patch.sh (new +x) | 105 ++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+)

diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 06c3c91762..cfd76bf987 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,28 +2,40 @@
 
 . ./test-lib.sh
 
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
 set_state () {
 	echo "$3" > "$1" &&
 	git add "$1" &&
 	echo "$2" > "$1"
 }
 
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
 save_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	cat "$1" > _worktree_"$noslash" &&
 	git show :"$1" > _index_"$noslash"
 }
 
+# set_and_save_state <path> <worktree-content> <index-content>
 set_and_save_state () {
 	set_state "$@" &&
 	save_state "$1"
 }
 
+# verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
 	test "$(cat "$1")" = "$2" &&
 	test "$(git show :"$1")" = "$3"
 }
 
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
 verify_saved_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
new file mode 100755
index 0000000000..df91bf54bc
--- /dev/null
+++ b/t/t2070-restore.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='restore basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	echo first-and-a-half >>first.t &&
+	git add first.t &&
+	test_commit second &&
+	echo one >one &&
+	echo two >two &&
+	echo untracked >untracked &&
+	echo ignored >ignored &&
+	echo /ignored >.gitignore &&
+	git add one two .gitignore &&
+	git update-ref refs/heads/one master
+'
+
+test_expect_success 'restore without pathspec is not ok' '
+	test_must_fail git restore &&
+	test_must_fail git restore --source=first
+'
+
+test_expect_success 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
+test_expect_success 'restore a file, ignoring branch of same name' '
+	cat one >expected &&
+	echo dirty >>one &&
+	git restore one &&
+	test_cmp expected one
+'
+
+test_expect_success 'restore a file on worktree from another branch' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first first.t &&
+	test_cmp expected first.t &&
+	git cat-file blob HEAD:./first.t >expected &&
+	git show :first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore a file in the index from another branch' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --index first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	git cat-file blob HEAD:./first.t >expected &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore a file in both the index and worktree from another branch' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --index --worktree first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore --index uses HEAD as source' '
+	test_when_finished git reset --hard &&
+	git cat-file blob :./first.t >expected &&
+	echo index-dirty >>first.t &&
+	git add first.t &&
+	git restore --index first.t &&
+	git cat-file blob :./first.t >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
new file mode 100755
index 0000000000..46ebcb2413
--- /dev/null
+++ b/t/t2071-restore-patch.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git restore --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent >dir/foo &&
+	echo dummy >bar &&
+	git add bar dir/foo &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n n | git restore -p &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git restore -p' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git restore -p with staged changes' '
+	set_state dir/foo work index &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD^' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD^ &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent index
+'
+
+test_expect_success PERL 'git restore -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent head
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | (cd dir && git restore -p foo) &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 10/11] completion: support restore
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (8 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 19:16   ` Elijah Newren
  2019-03-08 10:16 ` [PATCH v1 11/11] doc: promote "git restore" Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

Completion for restore is straightforward. We could still do better
though by give the list of just tracked files instead of all present
ones. But let's leave it for later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 7fcf28d437..0b22e68187 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2483,6 +2483,21 @@ _git_reset ()
 	__git_complete_refs
 }
 
+_git_restore ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--source=*)
+		__git_complete_refs --cur="${cur##--source=}"
+		;;
+	--*)
+		__gitcomp_builtin restore
+		;;
+	esac
+}
+
 __git_revert_inprogress_options="--continue --quit --abort"
 
 _git_revert ()
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v1 11/11] doc: promote "git restore"
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (9 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 10/11] completion: support restore Nguyễn Thái Ngọc Duy
@ 2019-03-08 10:16 ` Nguyễn Thái Ngọc Duy
  2019-03-09 19:37   ` Elijah Newren
  2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
  12 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08 10:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Elijah Newren, Nguyễn Thái Ngọc Duy

The new command "git restore" (together with "git switch") are added
to avoid the confusion of one-command-do-all "git checkout" for new
users. They are also helpful to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands.

One nice thing about git-restore is the ability to restore
"everything", so it can be used in "git status" advice instead of both
"git checkout" and "git reset".  The three commands suggested by "git
status" are add, rm and restore.

"git checkout" is also removed from "git help" (i.e. it's no longer
considered a commonly used command)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clean.txt        |  2 +-
 Documentation/git-commit.txt       |  4 +-
 Documentation/git-format-patch.txt |  2 +-
 Documentation/git-reset.txt        |  6 +--
 Documentation/git-revert.txt       |  4 +-
 Documentation/gitcli.txt           |  4 +-
 Documentation/giteveryday.txt      |  4 +-
 Documentation/gittutorial-2.txt    |  4 +-
 Documentation/gittutorial.txt      |  2 +-
 Documentation/user-manual.txt      | 14 +++--
 builtin/clone.c                    |  2 +-
 builtin/commit.c                   |  2 +-
 command-list.txt                   |  2 +-
 t/t7508-status.sh                  | 82 +++++++++++++++---------------
 t/t7512-status-help.sh             | 20 ++++----
 wt-status.c                        | 26 +++++++---
 16 files changed, 95 insertions(+), 85 deletions(-)

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 03056dad0d..8a31ccffea 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -64,7 +64,7 @@ OPTIONS
 	directory) and $GIT_DIR/info/exclude, but do still use the ignore
 	rules given with `-e` options.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
-	conjunction with 'git reset') to create a pristine
+	conjunction with 'git restore' or 'git reset') to create a pristine
 	working directory to test a clean build.
 
 -X::
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a85c2c2a4c..e7ac8eb9e8 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -56,7 +56,7 @@ summary of what is included by any of the above for the next
 commit by giving the same set of parameters (options and paths).
 
 If you make a commit and then find a mistake immediately after
-that, you can recover from it with 'git reset'.
+that, you can recover from it with 'git restore' or 'git reset'.
 
 
 OPTIONS
@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD -- <file>`,
+to that of the last commit with `git restore --index <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 0a24a5679e..71c9fe3af3 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:
 
     $ git fetch <project> master:test-apply
     $ git switch test-apply
-    $ git reset --hard
+    $ git restore --source=HEAD --index --worktree :/
     $ git am a.patch
 
 If it does not apply correctly, there can be various reasons.
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index cbf901efb4..b70281677f 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -28,9 +28,9 @@ This means that `git reset <paths>` is the opposite of `git add
 <paths>`.
 +
 After running `git reset <paths>` to update the index entry, you can
-use linkgit:git-checkout[1] to check the contents out of the index to
-the working tree.
-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+use linkgit:git-restore[1] to check the contents out of the index to
+the working tree. Alternatively, using linkgit:git-restore[1]
+and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 837707a8fd..c38bc54439 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the `--hard` option.  If
 you want to extract specific files as they were in another commit, you
-should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> -- <filename>` syntax.  Take care with these alternatives as
+should see linkgit:git-restore[1], specifically the `--source`
+option  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 OPTIONS
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 592e06d839..e2a9674297 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.
    things:
 +
 --------------------------------
-$ git checkout -- *.c
-$ git checkout -- \*.c
+$ git restore *.c
+$ git restore \*.c
 --------------------------------
 +
 The former lets your shell expand the fileglob, and you are asking
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index ad455f3e39..79517b22f9 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -51,7 +51,7 @@ following commands.
 
   * linkgit:git-commit[1] to advance the current branch.
 
-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with
+  * linkgit:git-reset[1] and linkgit:git-restore[1] (with
     pathname parameters) to undo changes.
 
   * linkgit:git-merge[1] to merge between local branches.
@@ -82,7 +82,7 @@ Create a topic branch and develop.::
 ------------
 $ git switch -c alsa-audio <1>
 $ edit/compile/test
-$ git checkout -- curses/ux_audio_oss.c <2>
+$ git restore curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
 $ edit/compile/test
 $ git diff HEAD <4>
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index e0976f6017..e412841488 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -370,13 +370,13 @@ situation:
 $ git status
 On branch master
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   closing.txt
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   file.txt
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index e6ad6b5f8d..6266ca21b4 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -110,7 +110,7 @@ $ git status
 On branch master
 Changes to be committed:
 Your branch is up to date with 'origin/master'.
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   file1
 	modified:   file2
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 94799faa2b..02713886f0 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess
 away, you can always return to the pre-merge state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git merge --abort
 -------------------------------------------------
 
 Or, if you've already committed the merge that you want to throw away,
@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed
 state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git restore --index --worktree :/
 -------------------------------------------------
 
 If you make a commit that you later wish you hadn't, there are two
@@ -1523,12 +1523,10 @@ Checking out an old version of a file
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used `git checkout` before to switch
-branches, but it has quite different behavior if it is given a path
-name: the command
+linkgit:git-restore[1]. The command
 
 -------------------------------------------------
-$ git checkout HEAD^ path/to/file
+$ git restore --source=HEAD^ path/to/file
 -------------------------------------------------
 
 replaces path/to/file by the contents it had in the commit HEAD^, and
@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.
 The Workflow
 ------------
 
-High-level operations such as linkgit:git-commit[1],
-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data
+High-level operations such as linkgit:git-commit[1] and
+linkgit:git-restore[1] work by moving data
 between the working tree, the index, and the object database.  Git
 provides low-level operations which perform each of these steps
 individually.
diff --git a/builtin/clone.c b/builtin/clone.c
index 50bde99618..024eb57996 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -492,7 +492,7 @@ static enum {
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry with 'git restore --source=HEAD :/'\n");
 
 static void remove_junk(void)
 {
diff --git a/builtin/commit.c b/builtin/commit.c
index f17537474a..c530264e63 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
-		      "not exceeded, and then \"git reset HEAD\" to recover."));
+		      "not exceeded, and then \"git restore --index :/\" to recover."));
 
 	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 		write_commit_graph_reachable(get_object_directory(), 0, 0);
diff --git a/command-list.txt b/command-list.txt
index b9eae1c258..cf8dccb439 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
 git-check-mailmap                       purehelpers
-git-checkout                            mainporcelain           history
+git-checkout                            mainporcelain
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
 git-cherry                              plumbinginterrogators          complete
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e1f11293e2..d5cf0185b7 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -94,13 +94,13 @@ test_expect_success 'status --column' '
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --index <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -128,13 +128,13 @@ cat >expect <<\EOF
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --index <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   ../dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   modified
 
@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	<GREEN>new file:   dir2/added<RESET>
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	<RED>modified:   dir1/modified<RESET>
 
@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   dir1/modified
 
@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD^1 <file>..." to unstage)
+  (use "git restore --source=HEAD^1 --index <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
   (commit or discard the untracked or modified content in submodules)
 
 	modified:   dir1/modified
@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 	modified:   sm (new commits)
@@ -1379,13 +1379,13 @@ cat > expect << EOF
 ;   (use "git pull" to merge the remote branch into yours)
 ;
 ; Changes to be committed:
-;   (use "git reset HEAD <file>..." to unstage)
+;   (use "git restore --index <file>..." to unstage)
 ;
 ;	modified:   sm
 ;
 ; Changes not staged for commit:
 ;   (use "git add <file>..." to update what will be committed)
-;   (use "git checkout -- <file>..." to discard changes in working directory)
+;   (use "git restore <file>..." to discard changes in working directory)
 ;
 ;	modified:   dir1/modified
 ;	modified:   sm (new commits)
@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 458608cc1e..6e678456bc 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -85,7 +85,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -110,7 +110,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -148,7 +148,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -176,7 +176,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\''split_commit'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   to-revert.txt
@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --index <file>..." to unstage)
 
 	modified:   to-revert.txt
 
diff --git a/wt-status.c b/wt-status.c
index 445a36204a..3098e77d72 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		;
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --index <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --index <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 
 	if (!both_deleted) {
@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c
+					 , _("  (use \"git restore --index <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --index <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,
 		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
 	else
 		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
-	status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+	status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
 		status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
 	status_printf_ln(s, c, "%s", "");
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
@ 2019-03-08 18:01   ` Elijah Newren
  2019-03-09 12:16     ` Duy Nguyen
  2019-03-25  9:53     ` Duy Nguyen
  2019-03-13  9:17   ` Johannes Schindelin
  1 sibling, 2 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-08 18:01 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

Thanks for working on this; overall looks really good.  I've got a few
comments here and there on the wording and combinations of options...

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> +SYNOPSIS

It might be worth adding some words somewhere to differentiate between
`reset` and `restore`.  E.g.

`git restore` modifies the working tree (and maybe index) to change
file content to match some other (usually older) version, but does not
update your branch.  `git reset` is about modifying which commit your
branch points to, meaning possibly removing and/or adding many commits
to your branch.

It may also make sense to add whatever description you use to the reset manpage.

> +--------
> +[verse]
> +'git restore' [<options>] [--source=<revision>] <pathspec>...
> +'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]

So one cannot specify any special options with -p?  Does that mean one
cannot use it with --index (i.e. this command cannot replace 'git
reset -p')?  Or is this an oversight in the synopsis?

> +DESCRIPTION
> +-----------
> +Restore paths in the working tree by replacing with the contents in
> +the restore source or remove if the paths do not exist in the restore
> +source. The source is by default the index but could be from a commit.
> +The command can also optionally restore content in the index from a
> +commit.

The first sentence makes it sound like two different operations, when
I think it is one. Perhaps:

Restore paths in the working tree by replacing with the contents in
the restore source (and if the restore source is missing paths found
in the working tree, that means deleting those paths from the working
tree).

> +
> +When a `<revision>` is given, the paths that match the `<pathspec>` are
> +updated both in the index and in the working tree.

I thought the default was --worktree.  Is this sentence from an older
version of your patch series that you forgot to update?

> +
> +OPTIONS
> +-------
> +-s<tree>::
> +--source=<tree>::
> +       Restore the working tree files with the content from the given
> +       tree or any revision that leads to a tree (e.g. a commit or a
> +       branch).

I think that's a little hard to parse.  We may not be able to avoid
"working tree" vs. "tree" confusion, but the spelling of <tree> feels
like it should be a separate sentence.  Maybe:

Restore the working tree files with the content from the given tree.
It is common to specify the source tree by naming a commit, branch, or
tag.

?

> +
> +-p::
> +--patch::
> +       Interactively select hunks in the difference between the
> +       `<revision>` (or the index, if unspecified) and the working
> +       tree. See the ``Interactive Mode'' section of linkgit:git-add[1]
> +       to learn how to operate the `--patch` mode.
> +
> +-W::
> +--worktree::
> +-I::
> +--index::
> +       Specify the restore location. If neither option is specified,
> +       by default the working tree is restored. If `--index` is
> +       specified without `--worktree` or `--source`, `--source=HEAD`
> +       is implied. These options only make sense to use with
> +       `--source`.

Seems like this contains a minor contradiction.  Perhaps start the
final sentence with: "Otherwise, ..." ?

> +-q::
> +--quiet::
> +       Quiet, suppress feedback messages.
> +
> +--progress::
> +--no-progress::
> +       Progress status is reported on the standard error stream
> +       by default when it is attached to a terminal, unless `--quiet`
> +       is specified. This flag enables progress reporting even if not
> +       attached to a terminal, regardless of `--quiet`.

I'm assuming this means there are feedback messages other than
progress feedback?

> +-f::
> +--force::
> +       If `--source` is not specified, unmerged entries are left alone
> +       and will not fail the operation. Unmerged entries are always
> +       replaced if `--source` is specified, regardless of `--force`.

This may be slightly confusing, in particular it suggests that --index
(or --worktree and --index) are the default.  Is --force only useful
when --index is specified?  If it has utility with --worktree only,
what does it do?  Also, what happens when there are unmerged entries
in the index and someone tries to restore just working tree files --
are the ones corresponding to unmerged entries skipped (if so,
silently or with warnings printed for the user?), or does something
else happen?

> +--ours::
> +--theirs::
> +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged
> +       paths.
> ++
> +Note that during `git rebase` and `git pull --rebase`, 'ours' and
> +'theirs' may appear swapped. See the explanation of the same options
> +in linkgit:git-checkout[1] for details.

So sad, but yes you need to mention it.  I'm curious what we say for
cherry-pick; it's not clear to me whether people think of the current
branch as "ours" or the commit they wrote themselves and are trying to
bring to this branch as "ours".  There are probably examples of both.

Not that I think you can do anything about that here or need to change
your description.  I'm just very sad about --ours and --theirs in
general.

> +
> +-m::
> +--merge::
> +       Recreate the conflicted merge in the specified paths.
> +
> +--conflict=<style>::
> +       The same as `--merge` option above, but changes the way the
> +       conflicting hunks are presented, overriding the merge.conflictStyle
> +       configuration variable.  Possible values are "merge" (default)
> +       and "diff3" (in addition to what is shown by "merge" style,
> +       shows the original contents).

Should you mention that these are incompatible with --source and
--index?  And perhaps also make sure the code aborts if either of
these options are combined with either of those?

> +--ignore-skip-worktree-bits::
> +       In sparse checkout mode, by default update only entries
> +       matched by `<pathspec>` and sparse patterns in
> +       $GIT_DIR/info/sparse-checkout. This option ignores the sparse
> +       patterns and unconditionally restores any files in `<pathspec>`.

The first sentence is slightly confusing; it sounds like you are
saying what the flag does rather than what the default is without the
flag.  Perhaps:

"In sparse checkout mode, the default is to only update entries
matched by `<pathspec>` and sparse patterns in
$GIT_DIR/info/sparse-checkout...."

> +
> +--recurse-submodules::
> +--no-recurse-submodules::
> +       Using `--recurse-submodules` will update the content of all initialized
> +       submodules according to the commit recorded in the superproject. If
> +       local modifications in a submodule would be overwritten the checkout
> +       will fail unless `-f` is used.

This suggests that your documentation for -f/--force is incomplete.

> +       If nothing (or `--no-recurse-submodules`)
> +       is used, the work trees of submodules will not be updated.

This seems slightly awkward.  Perhaps "If `--no-recurse-submodules`
(which is the default) is used, the work trees of submodules will not
be updated.

> +       Just like linkgit:git-submodule[1], this will detach the
> +       submodules HEAD.
> +
> +--overlay::
> +--no-overlay::
> +       In overlay mode, `git checkout` never removes files from the

Why are you talking about `git checkout` here?  Shouldn't this be `git restore`?

> +       index or the working tree. In no-overlay mode, files that
> +       appear in the index and working tree, but not in `--source` tree
> +       are removed, to make them match `<tree-ish>` exactly. The
> +       default is no-overlay mode.

A minor comment: This seems slightly weird because it feels like you
start out with a negative ("never removes") and then describe the
opposite option which adds another negation to the negative.  But the
wording avoids an explicit double negative (which is good), and I
don't have a better suggestion for wording here.  Just thought I'd
flag it in case anyone else reading over this notices it too and has a
wording suggestion.

> +
> +EXAMPLES
> +--------
> +
> +The following sequence checks out the `master` branch, reverts
> +the `Makefile` to two revisions back, deletes hello.c by
> +mistake, and gets it back from the index.
> +
> +------------
> +$ git switch master
> +$ git restore --source master~2 Makefile  <1>
> +$ rm -f hello.c
> +$ git restore hello.c                   <2>
> +------------
> +
> +<1> take a file out of another commit
> +<2> restore hello.c from the index
> +
> +If you want to check out _all_ C source files out of the index,
> +you can say
> +
> +------------
> +$ git restore '*.c'
> +------------
> +
> +Note the quotes around `*.c`.  The file `hello.c` will also be
> +checked out, even though it is no longer in the working tree,
> +because the file globbing is used to match entries in the index
> +(not in the working tree by the shell).
> +
> +To restore all files in the current directory
> +
> +------------
> +$ git restore .
> +------------
> +
> +or to restore all working tree files with 'top' pathspec magic (see
> +linkgit::gitglossary[7])
> +
> +------------
> +$ git restore :/
> +------------
> +
> +To restore a file in the index only (this is the same as using
> +"git reset")
> +
> +------------
> +$ git restore --index hello.c
> +------------
> +
> +or you can restore both the index and the working tree
> +
> +------------
> +$ git restore --source=HEAD --index --worktree hello.c
> +------------
> +
> +SEE ALSO
> +--------
> +linkgit:git-checkout[1]
> +
> +GIT
> +---
> +Part of the linkgit:git[1] suite
> diff --git a/Makefile b/Makefile
> index 8e91db73ad..ffe7e4f58f 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X
>  BUILT_INS += git-fsck-objects$X
>  BUILT_INS += git-init$X
>  BUILT_INS += git-merge-subtree$X
> +BUILT_INS += git-restore$X
>  BUILT_INS += git-show$X
>  BUILT_INS += git-stage$X
>  BUILT_INS += git-status$X
> diff --git a/builtin.h b/builtin.h
> index c64e44450e..6830000e14 100644
> --- a/builtin.h
> +++ b/builtin.h
> @@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
>  extern int cmd_repack(int argc, const char **argv, const char *prefix);
>  extern int cmd_rerere(int argc, const char **argv, const char *prefix);
>  extern int cmd_reset(int argc, const char **argv, const char *prefix);
> +extern int cmd_restore(int argc, const char **argv, const char *prefix);
>  extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
>  extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
>  extern int cmd_revert(int argc, const char **argv, const char *prefix);
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 4903359b49..11dd2ae44c 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -37,6 +37,11 @@ static const char * const switch_branch_usage[] = {
>         NULL,
>  };
>
> +static const char * const restore_files_usage[] = {
> +       N_("git restore [<options>] [<branch>] -- <file>..."),
> +       NULL,
> +};
> +
>  struct checkout_opts {
>         int patch_mode;
>         int quiet;
> @@ -1528,3 +1533,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>         FREE_AND_NULL(options);
>         return ret;
>  }
> +
> +int cmd_restore(int argc, const char **argv, const char *prefix)
> +{
> +       struct checkout_opts opts;
> +       struct option *options = NULL;
> +       int ret;
> +
> +       memset(&opts, 0, sizeof(opts));
> +       opts.dwim_new_local_branch = 1;
> +       opts.switch_branch_doing_nothing_is_ok = 0;
> +       opts.accept_pathspec = 1;
> +
> +       options = parse_options_dup(options);
> +       options = add_common_options(&opts, options);
> +       options = add_checkout_path_options(&opts, options);
> +
> +       ret = checkout_main(argc, argv, prefix, &opts,
> +                           options, restore_files_usage);
> +       FREE_AND_NULL(options);
> +       return ret;
> +}
> diff --git a/command-list.txt b/command-list.txt
> index 13317f47d4..b9eae1c258 100644
> --- a/command-list.txt
> +++ b/command-list.txt
> @@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete
>  git-request-pull                        foreignscminterface             complete
>  git-rerere                              ancillaryinterrogators
>  git-reset                               mainporcelain           worktree
> +git-restore                             mainporcelain           worktree
>  git-revert                              mainporcelain
>  git-rev-list                            plumbinginterrogators
>  git-rev-parse                           plumbinginterrogators
> diff --git a/git.c b/git.c
> index 39582cf511..6d439e723f 100644
> --- a/git.c
> +++ b/git.c
> @@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
>         { "replace", cmd_replace, RUN_SETUP },
>         { "rerere", cmd_rerere, RUN_SETUP },
>         { "reset", cmd_reset, RUN_SETUP },
> +       { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
>         { "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
>         { "rev-parse", cmd_rev_parse, NO_PARSEOPT },
>         { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
> --
> 2.21.0.rc1.337.gdf7f8d0522
>

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-08 18:01   ` Elijah Newren
@ 2019-03-09 12:16     ` Duy Nguyen
  2019-03-09 18:27       ` Elijah Newren
  2019-03-25  9:53     ` Duy Nguyen
  1 sibling, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-09 12:16 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano

On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:
>
> Thanks for working on this; overall looks really good.  I've got a few
> comments here and there on the wording and combinations of options...
>
> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > +SYNOPSIS
>
> It might be worth adding some words somewhere to differentiate between
> `reset` and `restore`.  E.g.
>
> `git restore` modifies the working tree (and maybe index) to change
> file content to match some other (usually older) version, but does not
> update your branch.  `git reset` is about modifying which commit your
> branch points to, meaning possibly removing and/or adding many commits
> to your branch.
>
> It may also make sense to add whatever description you use to the reset manpage.

Good point.

> > +--------
> > +[verse]
> > +'git restore' [<options>] [--source=<revision>] <pathspec>...
> > +'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]
>
> So one cannot specify any special options with -p?  Does that mean one
> cannot use it with --index (i.e. this command cannot replace 'git
> reset -p')?  Or is this an oversight in the synopsis?

Oversight. -p can be used with either --index or --worktree or both.

> > +
> > +When a `<revision>` is given, the paths that match the `<pathspec>` are
> > +updated both in the index and in the working tree.
>
> I thought the default was --worktree.  Is this sentence from an older
> version of your patch series that you forgot to update?

Oops.

> > +-q::
> > +--quiet::
> > +       Quiet, suppress feedback messages.
> > +
> > +--progress::
> > +--no-progress::
> > +       Progress status is reported on the standard error stream
> > +       by default when it is attached to a terminal, unless `--quiet`
> > +       is specified. This flag enables progress reporting even if not
> > +       attached to a terminal, regardless of `--quiet`.
>
> I'm assuming this means there are feedback messages other than
> progress feedback?

There could be. This is carried over from git-checkout. I suspect this
is about warnings that we print from time to time.

> > +-f::
> > +--force::
> > +       If `--source` is not specified, unmerged entries are left alone
> > +       and will not fail the operation. Unmerged entries are always
> > +       replaced if `--source` is specified, regardless of `--force`.
>
> This may be slightly confusing, in particular it suggests that --index
> (or --worktree and --index) are the default.  Is --force only useful
> when --index is specified?  If it has utility with --worktree only,
> what does it do?

Well, this is 'git checkout -f' behavior which only concerns the
index. So yeah it only matters with --index.

> Also, what happens when there are unmerged entries
> in the index and someone tries to restore just working tree files --
> are the ones corresponding to unmerged entries skipped (if so,
> silently or with warnings printed for the user?), or does something
> else happen?

If -m is also specified, then we recreate the conflict. The from code,
if an unmerged path is skipped, there will be warnings.

> > +
> > +-m::
> > +--merge::
> > +       Recreate the conflicted merge in the specified paths.
> > +
> > +--conflict=<style>::
> > +       The same as `--merge` option above, but changes the way the
> > +       conflicting hunks are presented, overriding the merge.conflictStyle
> > +       configuration variable.  Possible values are "merge" (default)
> > +       and "diff3" (in addition to what is shown by "merge" style,
> > +       shows the original contents).
>
> Should you mention that these are incompatible with --source and
> --index?  And perhaps also make sure the code aborts if either of
> these options are combined with either of those?

I will make sure that the code aborts. Not so sure about mentionging
every incompatible combination though. Will it be too much? I think we
catch and report plenty invalid combinations but I don't think we have
mentioned them all (or maybe we have, I didn't check the document
again)

> > +       Just like linkgit:git-submodule[1], this will detach the
> > +       submodules HEAD.
> > +
> > +--overlay::
> > +--no-overlay::
> > +       In overlay mode, `git checkout` never removes files from the
>
> Why are you talking about `git checkout` here?  Shouldn't this be `git restore`?

Oops.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
@ 2019-03-09 16:53   ` Andrei Rybak
  2019-03-13  9:13   ` Johannes Schindelin
  2019-03-14  5:45   ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Andrei Rybak @ 2019-03-09 16:53 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano, Elijah Newren

On 3/8/19 11:16 AM, Nguyễn Thái Ngọc Duy wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  t/lib-patch-mode.sh               |  12 ++++
>  t/t2070-restore.sh (new +x)       |  77 ++++++++++++++++++++++
>  t/t2071-restore-patch.sh (new +x) | 105 ++++++++++++++++++++++++++++++
>  3 files changed, 194 insertions(+)
> 
> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
> new file mode 100755
> index 0000000000..df91bf54bc
> --- /dev/null
> +++ b/t/t2070-restore.sh
> @@ -0,0 +1,77 @@
> +#!/bin/sh
> +
> +test_description='restore basic functionality'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> +	test_commit first &&
> +	echo first-and-a-half >>first.t &&
> +	git add first.t &&
> +	test_commit second &&
> +	echo one >one &&
> +	echo two >two &&
> +	echo untracked >untracked &&
> +	echo ignored >ignored &&
> +	echo /ignored >.gitignore &&
> +	git add one two .gitignore &&
> +	git update-ref refs/heads/one master
> +'
> +

[snip]

> +
> +test_expect_success 'restore a file, ignoring branch of same name' '
> +	cat one >expected &&
> +	echo dirty >>one &&
> +	git restore one &&
> +	test_cmp expected one
> +'
> +

Branch 'one' has been created by update-ref invocation in the setup, OK.

> +test_expect_success 'restore a file on worktree from another branch' '
> +	test_when_finished git reset --hard &&
> +	git cat-file blob first:./first.t >expected &&
> +	git restore --source=first first.t &&
> +	test_cmp expected first.t &&
> +	git cat-file blob HEAD:./first.t >expected &&
> +	git show :first.t >actual &&
> +	test_cmp expected actual
> +'

Test description reads "from another branch". However "first", created by
test_commit invocation is a tag. Maybe description should read "from another
ref"? Same applies to other tests which utilize "first".

> +
> +test_expect_success 'restore a file in the index from another branch' '
> +	test_when_finished git reset --hard &&
> +	git cat-file blob first:./first.t >expected &&
> +	git restore --source=first --index first.t &&
> +	git show :first.t >actual &&
> +	test_cmp expected actual &&
> +	git cat-file blob HEAD:./first.t >expected &&
> +	test_cmp expected first.t
> +'
> +
> +test_expect_success 'restore a file in both the index and worktree from another branch' '
> +	test_when_finished git reset --hard &&
> +	git cat-file blob first:./first.t >expected &&
> +	git restore --source=first --index --worktree first.t &&
> +	git show :first.t >actual &&
> +	test_cmp expected actual &&
> +	test_cmp expected first.t
> +'
> +
> +test_expect_success 'restore --index uses HEAD as source' '
> +	test_when_finished git reset --hard &&
> +	git cat-file blob :./first.t >expected &&
> +	echo index-dirty >>first.t &&
> +	git add first.t &&
> +	git restore --index first.t &&
> +	git cat-file blob :./first.t >actual &&
> +	test_cmp expected actual
> +'
> +
> +test_done


--
Best regards, Andrei R.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 02/11] restore: take tree-ish from --source option instead
  2019-03-08 10:16 ` [PATCH v1 02/11] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
@ 2019-03-09 18:13   ` Elijah Newren
  2019-03-10  7:58   ` Eric Sunshine
  1 sibling, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:13 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> This is another departure from 'git checkout' syntax, which uses -- to
> separate ref and pathspec. The observation is restore (or "git
> checkout ,, <pathspec>") is most often used to restore some files from
> the index. If this is correct, we can simplify it by taking a way the
> ref, so that we can write
>
>     git restore some-file
>
> without worrying about some-file being a ref and whether we need to do
>
>     git restore -- some-file
>
> for safety. If the source of the restore comes from a tree, it will be
> in the form of an option with value, e.g.
>
>     git restore --source=this-tree some-file
>
> This is of course longer to type than using "--". But hopefully it
> will not be used as often, and it is clearly easier to understand.
>
> dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()
> because it's irrelevant because we don't really care about dwim-ing.
> With accept_ref being unset, dwim can't happen.

Nice.  :-)

>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------
>  1 file changed, 34 insertions(+), 8 deletions(-)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 11dd2ae44c..838343d6aa 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {
>  };
>
>  static const char * const restore_files_usage[] = {

restore_files_usage or restore_usage?

> -       N_("git restore [<options>] [<branch>] -- <file>..."),
> +       N_("git restore [<options>] [--source=<branch>] <file>..."),
>         NULL,
>  };
>
> @@ -57,6 +57,7 @@ struct checkout_opts {
>         int count_checkout_paths;
>         int overlay_mode;
>         int dwim_new_local_branch;
> +       int accept_ref;
>         int accept_pathspec;
>         int switch_branch_doing_nothing_is_ok;
>         int only_merge_on_switching_branches;
> @@ -72,6 +73,7 @@ struct checkout_opts {
>         int branch_exists;
>         const char *prefix;
>         struct pathspec pathspec;
> +       const char *from_treeish;
>         struct tree *source_tree;
>  };
>
> @@ -1324,6 +1326,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>  {
>         struct branch_info new_branch_info;
>         int dwim_remotes_matched = 0;
> +       int parseopt_flags = 0;
>
>         memset(&new_branch_info, 0, sizeof(new_branch_info));
>         opts->overwrite_ignore = 1;
> @@ -1335,8 +1338,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>
>         opts->track = BRANCH_TRACK_UNSPECIFIED;
>
> -       argc = parse_options(argc, argv, prefix, options, usagestr,
> -                            PARSE_OPT_KEEP_DASHDASH);
> +       if (!opts->accept_pathspec && !opts->accept_ref)
> +               BUG("make up your mind, you need to take _something_");

hehe.  I secretly hope that someone eventually hits that, preferably
another git developer making some changes and not an end user, though.


> +       if (opts->accept_pathspec && opts->accept_ref)
> +               parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
> +
> +       argc = parse_options(argc, argv, prefix, options,
> +                            usagestr, parseopt_flags);
>
>         if (opts->show_progress < 0) {
>                 if (opts->quiet)
> @@ -1393,7 +1401,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>          * including "last branch" syntax and DWIM-ery for names of
>          * remote branches, erroring out for invalid or ambiguous cases.
>          */
> -       if (argc) {
> +       if (argc && opts->accept_ref) {
>                 struct object_id rev;
>                 int dwim_ok =
>                         !opts->patch_mode &&
> @@ -1405,6 +1413,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>                                              &dwim_remotes_matched);
>                 argv += n;
>                 argc -= n;
> +       } else if (!opts->accept_ref && opts->from_treeish) {
> +               struct object_id rev;
> +
> +               if (get_oid_mb(opts->from_treeish, &rev))

Going on a slight tangent from your series: get_oid_mb really deserves
a comment somewhere that "mb" is "merge base".  I had to spelunk
through history to find it...

> +                       die(_("could not resolve %s"), opts->from_treeish);
> +
> +               setup_new_branch_info_and_source_tree(&new_branch_info,
> +                                                     opts, &rev,
> +                                                     opts->from_treeish);
> +
> +               if (!opts->source_tree)
> +                       die(_("reference is not a tree: %s"), opts->from_treeish);
>         }
>
>         if (argc) {
> @@ -1488,6 +1508,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>         opts.dwim_new_local_branch = 1;
>         opts.switch_branch_doing_nothing_is_ok = 1;
>         opts.only_merge_on_switching_branches = 0;
> +       opts.accept_ref = 1;
>         opts.accept_pathspec = 1;
>         opts.implicit_detach = 1;
>
> @@ -1519,6 +1540,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>
>         memset(&opts, 0, sizeof(opts));
>         opts.dwim_new_local_branch = 0;
> +       opts.accept_ref = 1;
>         opts.accept_pathspec = 0;
>         opts.switch_branch_doing_nothing_is_ok = 0;
>         opts.only_merge_on_switching_branches = 1;
> @@ -1537,15 +1559,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>  int cmd_restore(int argc, const char **argv, const char *prefix)
>  {
>         struct checkout_opts opts;
> -       struct option *options = NULL;
> +       struct option *options;
> +       struct option restore_options[] = {
> +               OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
> +                          N_("where the checkout from")),
> +               OPT_END()
> +       };
>         int ret;
>
>         memset(&opts, 0, sizeof(opts));
> -       opts.dwim_new_local_branch = 1;
> -       opts.switch_branch_doing_nothing_is_ok = 0;
> +       opts.accept_ref = 0;
>         opts.accept_pathspec = 1;
>
> -       options = parse_options_dup(options);
> +       options = parse_options_dup(restore_options);
>         options = add_common_options(&opts, options);
>         options = add_checkout_path_options(&opts, options);
>
> --
> 2.21.0.rc1.337.gdf7f8d0522
>

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-09 12:16     ` Duy Nguyen
@ 2019-03-09 18:27       ` Elijah Newren
  2019-04-09 12:18         ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:27 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Junio C Hamano

On Sat, Mar 9, 2019 at 4:16 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:

> > > +-q::
> > > +--quiet::
> > > +       Quiet, suppress feedback messages.
> > > +
> > > +--progress::
> > > +--no-progress::
> > > +       Progress status is reported on the standard error stream
> > > +       by default when it is attached to a terminal, unless `--quiet`
> > > +       is specified. This flag enables progress reporting even if not
> > > +       attached to a terminal, regardless of `--quiet`.
> >
> > I'm assuming this means there are feedback messages other than
> > progress feedback?
>
> There could be. This is carried over from git-checkout. I suspect this
> is about warnings that we print from time to time.

Why would --quiet squelch warnings?  I figured it'd only squelch
feedback, informational, or progress messages.

I understand you just carried it over from git-checkout, but as worded
it makes me wonder if checkout has suboptimal behavior or perhaps just
a suboptimal explanation of its flags ... and if it does, we probably
don't want to carry that over.

> > > +-f::
> > > +--force::
> > > +       If `--source` is not specified, unmerged entries are left alone
> > > +       and will not fail the operation. Unmerged entries are always
> > > +       replaced if `--source` is specified, regardless of `--force`.
> >
> > This may be slightly confusing, in particular it suggests that --index
> > (or --worktree and --index) are the default.  Is --force only useful
> > when --index is specified?  If it has utility with --worktree only,
> > what does it do?
>
> Well, this is 'git checkout -f' behavior which only concerns the
> index. So yeah it only matters with --index.

Okay, good to know that this only matters with --index. However, new
problem: This makes the explanation feel contradictory, though,
because elsewhere you stated that --source=HEAD is implied when
--index is given without a source.  So, the combination of this
description and that fact suggests that -f is either useless (--index
is not specified) or ignored (because --source will either default to
HEAD or be specified by the user).  Maybe that's true and -f should be
removed from this new document.  If it has actual utility in some
cases, then this description needs to be reworked to explain what
those circiumstances are somehow.

> > Also, what happens when there are unmerged entries
> > in the index and someone tries to restore just working tree files --
> > are the ones corresponding to unmerged entries skipped (if so,
> > silently or with warnings printed for the user?), or does something
> > else happen?
>
> If -m is also specified, then we recreate the conflict. The from code,
> if an unmerged path is skipped, there will be warnings.

Not sure I understand.  Are you saying that if -m is not specified and
nor is --source or --index, that we print a warning for each unmerged
entry specified by the pathspecs?

> > > +
> > > +-m::
> > > +--merge::
> > > +       Recreate the conflicted merge in the specified paths.
> > > +
> > > +--conflict=<style>::
> > > +       The same as `--merge` option above, but changes the way the
> > > +       conflicting hunks are presented, overriding the merge.conflictStyle
> > > +       configuration variable.  Possible values are "merge" (default)
> > > +       and "diff3" (in addition to what is shown by "merge" style,
> > > +       shows the original contents).
> >
> > Should you mention that these are incompatible with --source and
> > --index?  And perhaps also make sure the code aborts if either of
> > these options are combined with either of those?
>
> I will make sure that the code aborts. Not so sure about mentioning
> every incompatible combination though. Will it be too much? I think we
> catch and report plenty invalid combinations but I don't think we have
> mentioned them all (or maybe we have, I didn't check the document
> again)

Making sure the code aborts is probably good enough.


Elijah

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 03/11] restore: make pathspec mandatory
  2019-03-08 10:16 ` [PATCH v1 03/11] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
@ 2019-03-09 18:35   ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:35 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> "git restore" without arguments does not make much sense when
> it's about restoring files (what files now?). We could default to
> either
>
>     git restore .
>
> or
>
>     git restore :/
>
> Neither is intuitive. Make the user always give pathspec, force the
> user to think the scope of restore they want because this is a
> destructive operation.
>
> "git restore -p" without pathspec is an exception to this
> because it really is a separate mode. It will be treated as running
> patch mode on the whole worktree.

This all sounds very good.

>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 838343d6aa..c52ce13d2a 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -61,6 +61,7 @@ struct checkout_opts {
>         int accept_pathspec;
>         int switch_branch_doing_nothing_is_ok;
>         int only_merge_on_switching_branches;
> +       int empty_pathspec_ok;
>
>         const char *new_branch;
>         const char *new_branch_force;
> @@ -1427,6 +1428,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>                         die(_("reference is not a tree: %s"), opts->from_treeish);
>         }
>
> +       if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
> +           !opts->patch_mode)  /* patch mode is special */
> +               die(_("pathspec is required"));

Maybe
   die(_("you must specify path(s) to restore"));
?  While "pathspec" is something we use in a few places, I don't think
users intuitively know what it is.  Also, I just looked up the manpage
again, and it looks like you use "pathspec" in the restore manpage,
but don't define it.

> +
>         if (argc) {
>                 parse_pathspec(&opts->pathspec, 0,
>                                opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
> @@ -1511,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>         opts.accept_ref = 1;
>         opts.accept_pathspec = 1;
>         opts.implicit_detach = 1;
> +       opts.empty_pathspec_ok = 1;
>
>         options = parse_options_dup(checkout_options);
>         options = add_common_options(&opts, options);
> @@ -1570,6 +1576,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
>         memset(&opts, 0, sizeof(opts));
>         opts.accept_ref = 0;
>         opts.accept_pathspec = 1;
> +       opts.empty_pathspec_ok = 0;
>
>         options = parse_options_dup(restore_options);
>         options = add_common_options(&opts, options);
> --
> 2.21.0.rc1.337.gdf7f8d0522

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 04/11] restore: disable overlay mode by default
  2019-03-08 10:16 ` [PATCH v1 04/11] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
@ 2019-03-09 18:37   ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:37 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Overlay mode is considered confusing when the command is about
> restoring files on worktree. Disable it by default. The user can still
> turn it on, or use 'git checkout' which still has overlay mode on by
> default.
>
> While at there make the check in checkout_branch() stricter. Neither
> --overlay or --no-overlay should be accepted in branch switching mode.

s/While at there/While at it,/ ?

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 11 +++++++----
>  1 file changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index c52ce13d2a..9e59bf792f 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -1196,9 +1196,9 @@ static int checkout_branch(struct checkout_opts *opts,
>                 die(_("'%s' cannot be used with switching branches"),
>                     "--patch");
>
> -       if (!opts->overlay_mode)
> +       if (opts->overlay_mode != -1)
>                 die(_("'%s' cannot be used with switching branches"),
> -                   "--no-overlay");
> +                   "--[no]-overlay");
>
>         if (opts->writeout_stage)
>                 die(_("'%s' cannot be used with switching branches"),
> @@ -1313,7 +1313,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
>                 OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
>                 OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
>                          N_("do not limit pathspecs to sparse entries only")),
> -               OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
>                 OPT_END()
>         };
>         struct option *newopts = parse_options_concat(prevopts, options);
> @@ -1333,7 +1332,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>         opts->overwrite_ignore = 1;
>         opts->prefix = prefix;
>         opts->show_progress = -1;
> -       opts->overlay_mode = -1;
>
>         git_config(git_checkout_config, opts);
>
> @@ -1505,6 +1503,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>                 OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
>                 OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
>                          N_("second guess 'git checkout <no-such-branch>' (default)")),
> +               OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
>                 OPT_END()
>         };
>         int ret;
> @@ -1517,6 +1516,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>         opts.accept_pathspec = 1;
>         opts.implicit_detach = 1;
>         opts.empty_pathspec_ok = 1;
> +       opts.overlay_mode = -1;
>
>         options = parse_options_dup(checkout_options);
>         options = add_common_options(&opts, options);
> @@ -1551,6 +1551,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>         opts.switch_branch_doing_nothing_is_ok = 0;
>         opts.only_merge_on_switching_branches = 1;
>         opts.implicit_detach = 0;
> +       opts.overlay_mode = -1;
>
>         options = parse_options_dup(switch_options);
>         options = add_common_options(&opts, options);
> @@ -1569,6 +1570,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
>         struct option restore_options[] = {
>                 OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
>                            N_("where the checkout from")),
> +               OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
>                 OPT_END()
>         };
>         int ret;
> @@ -1577,6 +1579,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
>         opts.accept_ref = 0;
>         opts.accept_pathspec = 1;
>         opts.empty_pathspec_ok = 0;
> +       opts.overlay_mode = 0;
>
>         options = parse_options_dup(restore_options);
>         options = add_common_options(&opts, options);
> --
> 2.21.0.rc1.337.gdf7f8d0522

The rest all looks good.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 06/11] restore: add --worktree and --index
  2019-03-08 10:16 ` [PATCH v1 06/11] restore: add --worktree and --index Nguyễn Thái Ngọc Duy
@ 2019-03-09 18:52   ` Elijah Newren
  2019-03-10 20:03     ` Eric Sunshine
                       ` (2 more replies)
  0 siblings, 3 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:52 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> 'git checkout <tree-ish> <pathspec>' updates both index and
> worktree. But updating the index when you want to restore worktree
> files is non-intuitive. The index contains the data ready for the next
> commit, and there's no indication that the user will want to commit
> the restored versions.
>
> 'git restore' therefore by default only touches worktree. The user has
> the option to update either the index with
>
>     git restore --source=<tree> --index <path>  (1)
>
> or update both with
>
>     git restore --source=<tree> --index --worktree <path> (2)
>
> PS. Orignally I wanted to make worktree update default and form (1)
> would add index update while also updating the worktree, and the user
> would need to do "--index --no-worktree" to update index only. But it
> looks really confusing that "--index" option alone updates both. So
> now form (2) is used for both, which reads much more obvious.
>
> PPS. Yes form (1) overlaps with "git reset <rev> <path>". I don't know
> if we can ever turn "git reset" back to "_always_ reset HEAD and
> optionally do something else".

I'm really happy with how this series is going generally.  :-)

> +       if (!opts->checkout_worktree && !opts->checkout_index)
> +               die(_("neither '%s' or '%s' is specified"),
> +                   "--index", "--worktree");

Is this die() or BUG()?  I thought --worktree was the default.

> +               else
> +                       die(_("'%s' with only '%s' is not currently supported"),
> +                           "--patch", "--worktree");

:-(

> +               /*
> +                * NEEDSWORK: if --worktree is not specified, we
> +                * should save stat info of checked out files in the
> +                * index to avoid the next (potentially costly)
> +                * refresh. But it's a bit tricker to do...
> +                */
> +               rollback_lock_file(&lock_file);

A total tangent: I see both FIXME and NEEDSWORK in the codebase.  Are
there other 'keywords' of this type that we use?  Is there a
preference for how they are used?

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified
  2019-03-08 10:16 ` [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified Nguyễn Thái Ngọc Duy
@ 2019-03-09 18:58   ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 18:58 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> "git restore --index" does not make much sense since we're told to
> restore the index from the (by default) index. Set default source to
> HEAD in this case. This is basically the same as "git reset -- <paths>".

Can I suggest a slight rewording to the commit message?

"git restore --index" without --source does not make much sense since by
default we restore from the index.  Instead of copying the index to
itself, set the default source to HEAD in this case, yielding behavior
that matches "git reset -- <paths>".


The rest looks good to me.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 08/11] restore: support --patch
  2019-03-08 10:16 ` [PATCH v1 08/11] restore: support --patch Nguyễn Thái Ngọc Duy
@ 2019-03-09 19:02   ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 19:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> git-restore is different from git-checkout that it only restores the
> worktree by default, not both worktree and index. add--interactive
> needs some update to support this mode.

Nice!  That removes my frowny face from a few patches before...  :-)

> @@ -436,9 +436,10 @@ static int checkout_paths(const struct checkout_opts *opts,
>                         patch_mode = "--patch=checkout";
>                 else if (opts->checkout_index && !opts->checkout_worktree)
>                         patch_mode = "--patch=reset";
> +               else if (!opts->checkout_index && opts->checkout_worktree)
> +                       patch_mode = "--patch=worktree";
>                 else
> -                       die(_("'%s' with only '%s' is not currently supported"),
> -                           "--patch", "--worktree");
> +                       BUG("either flag must have been set");

Very minor nit: I suspect we'll never hit this, but do we want to make
the error message slightly more descriptive ("which flags"?) in case
someone doing refactoring work does?

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 10/11] completion: support restore
  2019-03-08 10:16 ` [PATCH v1 10/11] completion: support restore Nguyễn Thái Ngọc Duy
@ 2019-03-09 19:16   ` Elijah Newren
  2019-03-11 15:22     ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 19:16 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Completion for restore is straightforward. We could still do better
> though by give the list of just tracked files instead of all present
> ones. But let's leave it for later.

s/give/giving/

I'm slightly worried that due to using --no-overlay mode by default in
restore, having tab-completion include untracked files increases the
risk of accidentally nuking the wrong file.  restore is a destructive
command anyway and should thus be used with care, so perhaps this
isn't a big deal, but I thought I'd mention it.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 11/11] doc: promote "git restore"
  2019-03-08 10:16 ` [PATCH v1 11/11] doc: promote "git restore" Nguyễn Thái Ngọc Duy
@ 2019-03-09 19:37   ` Elijah Newren
  2019-03-11 14:11     ` Randall S. Becker
  0 siblings, 1 reply; 97+ messages in thread
From: Elijah Newren @ 2019-03-09 19:37 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git Mailing List, Junio C Hamano

On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> The new command "git restore" (together with "git switch") are added
> to avoid the confusion of one-command-do-all "git checkout" for new
> users. They are also helpful to avoid ambiguation context.

s/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?

> For these reasons, promote it everywhere possible. This includes
> documentation, suggestions/advice from other commands.
>
> One nice thing about git-restore is the ability to restore
> "everything", so it can be used in "git status" advice instead of both
> "git checkout" and "git reset".  The three commands suggested by "git
> status" are add, rm and restore.
>
> "git checkout" is also removed from "git help" (i.e. it's no longer
> considered a commonly used command)
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

> @@ -56,7 +56,7 @@ summary of what is included by any of the above for the next
>  commit by giving the same set of parameters (options and paths).
>
>  If you make a commit and then find a mistake immediately after
> -that, you can recover from it with 'git reset'.
> +that, you can recover from it with 'git restore' or 'git reset'.

Does 'git revert' also belong in this set?  And do we need to extend
our blurb differentiating reset and restore to also include revert?

> @@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to
>  throw away all uncommitted changes in your working directory, you
>  should see linkgit:git-reset[1], particularly the `--hard` option.  If
>  you want to extract specific files as they were in another commit, you
> -should see linkgit:git-checkout[1], specifically the `git checkout
> -<commit> -- <filename>` syntax.  Take care with these alternatives as
> +should see linkgit:git-restore[1], specifically the `--source`
> +option  Take care with these alternatives as
>  both will discard uncommitted changes in your working directory.

Missing period after "option".

>  -------------------------------------------------
> -$ git reset --hard HEAD
> +$ git merge --abort

Nice additional cleanup, but should it be in a separate patch since it
isn't about the restore command?


Elijah

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 02/11] restore: take tree-ish from --source option instead
  2019-03-08 10:16 ` [PATCH v1 02/11] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
  2019-03-09 18:13   ` Elijah Newren
@ 2019-03-10  7:58   ` Eric Sunshine
  1 sibling, 0 replies; 97+ messages in thread
From: Eric Sunshine @ 2019-03-10  7:58 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Elijah Newren

On Fri, Mar 8, 2019 at 5:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This is another departure from 'git checkout' syntax, which uses -- to
> separate ref and pathspec. The observation is restore (or "git
> checkout ,, <pathspec>") is most often used to restore some files from

What is the ",," thing?

> the index. If this is correct, we can simplify it by taking a way the

s/a way/away/

> ref, so that we can write
>
>     git restore some-file
>
> without worrying about some-file being a ref and whether we need to do
>
>     git restore -- some-file
>
> for safety. If the source of the restore comes from a tree, it will be
> in the form of an option with value, e.g.
>
>     git restore --source=this-tree some-file
>
> This is of course longer to type than using "--". But hopefully it
> will not be used as often, and it is clearly easier to understand.
>
> dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()
> because it's irrelevant because we don't really care about dwim-ing.
> With accept_ref being unset, dwim can't happen.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 00/11] And new command "restore"
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (10 preceding siblings ...)
  2019-03-08 10:16 ` [PATCH v1 11/11] doc: promote "git restore" Nguyễn Thái Ngọc Duy
@ 2019-03-10 11:19 ` Duy Nguyen
  2019-03-10 22:45   ` Jacob Keller
                     ` (2 more replies)
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
  12 siblings, 3 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-03-10 11:19 UTC (permalink / raw)
  To: Git Mailing List, Elijah Newren, Junio C Hamano, Eric Sunshine,
	Andrei Rybak

On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> - --index has a different meaning in git-apply. I don't have a good
>   suggestion except "yeah we have two different worlds now, the old
>   and the new one"

I will rename --index to --staged to avoid this. It is already used as
synonym for --cached in git-diff. I will also add --staged as synonym
to "git rm" so that "git status" can consistently advise "git <verb>
--staged" where verb is either restore or rm.

Since option rename is a lot of work. If you think this is not a good
move, raise it now, even if it's just "I don't like it, no reasons".
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 06/11] restore: add --worktree and --index
  2019-03-09 18:52   ` Elijah Newren
@ 2019-03-10 20:03     ` Eric Sunshine
  2019-03-13 10:02     ` Duy Nguyen
  2019-03-13 22:42     ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Eric Sunshine @ 2019-03-10 20:03 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Nguyễn Thái Ngọc Duy, Git Mailing List, Junio C Hamano

On Sat, Mar 9, 2019 at 1:52 PM Elijah Newren <newren@gmail.com> wrote:
> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > +       if (!opts->checkout_worktree && !opts->checkout_index)
> > +               die(_("neither '%s' or '%s' is specified"),
> > +                   "--index", "--worktree");
>
> Is this die() or BUG()?  I thought --worktree was the default.

The user might type "git restore --no-worktree --no-index" which would
trigger this error message, so definitely die(), not a BUG().

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 00/11] And new command "restore"
  2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
@ 2019-03-10 22:45   ` Jacob Keller
  2019-03-11 16:01   ` Elijah Newren
  2019-03-13 22:58   ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Jacob Keller @ 2019-03-10 22:45 UTC (permalink / raw)
  To: Duy Nguyen, Git Mailing List, Elijah Newren, Junio C Hamano,
	Eric Sunshine, Andrei Rybak



On March 10, 2019 4:19:28 AM PDT, Duy Nguyen <pclouds@gmail.com> wrote:
>On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>wrote:
>> - --index has a different meaning in git-apply. I don't have a good
>>   suggestion except "yeah we have two different worlds now, the old
>>   and the new one"
>
>I will rename --index to --staged to avoid this. It is already used as
>synonym for --cached in git-diff. I will also add --staged as synonym
>to "git rm" so that "git status" can consistently advise "git <verb>
>--staged" where verb is either restore or rm.
>
>Since option rename is a lot of work. If you think this is not a good
>move, raise it now, even if it's just "I don't like it, no reasons".

For what it's worth, I think this is a good rename.

Thanks,
Jake

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* RE: [PATCH v1 11/11] doc: promote "git restore"
  2019-03-09 19:37   ` Elijah Newren
@ 2019-03-11 14:11     ` Randall S. Becker
  2019-03-11 14:36       ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Randall S. Becker @ 2019-03-11 14:11 UTC (permalink / raw)
  To: Elijah Newren, Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano

> -----Original Message-----
> From: git-owner@vger.kernel.org <git-owner@vger.kernel.org> On Behalf
> Of Elijah Newren
> Sent: March 9, 2019 14:38
> To: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> Cc: Git Mailing List <git@vger.kernel.org>; Junio C Hamano
> <gitster@pobox.com>
> Subject: Re: [PATCH v1 11/11] doc: promote "git restore"
> 
> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy
> <pclouds@gmail.com> wrote:
> >
> > The new command "git restore" (together with "git switch") are added
> > to avoid the confusion of one-command-do-all "git checkout" for new
> > users. They are also helpful to avoid ambiguation context.
> 
> s/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?
> 
> > For these reasons, promote it everywhere possible. This includes
> > documentation, suggestions/advice from other commands.
> >
> > One nice thing about git-restore is the ability to restore
> > "everything", so it can be used in "git status" advice instead of both
> > "git checkout" and "git reset".  The three commands suggested by "git
> > status" are add, rm and restore.
> >
> > "git checkout" is also removed from "git help" (i.e. it's no longer
> > considered a commonly used command)

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.


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 11/11] doc: promote "git restore"
  2019-03-11 14:11     ` Randall S. Becker
@ 2019-03-11 14:36       ` Duy Nguyen
  2019-03-13  4:58         ` Junio C Hamano
  0 siblings, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-11 14:36 UTC (permalink / raw)
  To: Randall S. Becker; +Cc: Elijah Newren, Git Mailing List, Junio C Hamano

On Mon, Mar 11, 2019 at 9:12 PM Randall S. Becker
<rsbecker@nexbridge.com> wrote:
>
> > -----Original Message-----
> > From: git-owner@vger.kernel.org <git-owner@vger.kernel.org> On Behalf
> > Of Elijah Newren
> > Sent: March 9, 2019 14:38
> > To: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> > Cc: Git Mailing List <git@vger.kernel.org>; Junio C Hamano
> > <gitster@pobox.com>
> > Subject: Re: [PATCH v1 11/11] doc: promote "git restore"
> >
> > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy
> > <pclouds@gmail.com> wrote:
> > >
> > > The new command "git restore" (together with "git switch") are added
> > > to avoid the confusion of one-command-do-all "git checkout" for new
> > > users. They are also helpful to avoid ambiguation context.
> >
> > s/ambiguation/ambiguous/ or s/ambiguation context/ambiguity/?
> >
> > > For these reasons, promote it everywhere possible. This includes
> > > documentation, suggestions/advice from other commands.
> > >
> > > One nice thing about git-restore is the ability to restore
> > > "everything", so it can be used in "git status" advice instead of both
> > > "git checkout" and "git reset".  The three commands suggested by "git
> > > status" are add, rm and restore.
> > >
> > > "git checkout" is also removed from "git help" (i.e. it's no longer
> > > considered a commonly used command)
>
> 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.

I declare "git checkout" not commonly used the moment git-switch and
git-restore are released!

More seriously, this is an attempt to promote new commands. The
"commonly used" here refers to the set of commands that show when you
run "git help" (they are called "common" in command-list.txt
classification). Since the people who type "git help" are mostly new
people, I think promoting the friendlier alternative is a good idea.
And no neither switch or restore has been released. They are not even
in 'pu' yet.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 10/11] completion: support restore
  2019-03-09 19:16   ` Elijah Newren
@ 2019-03-11 15:22     ` Duy Nguyen
  2019-03-11 15:39       ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-11 15:22 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano

On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> >
> > Completion for restore is straightforward. We could still do better
> > though by give the list of just tracked files instead of all present
> > ones. But let's leave it for later.
>
> s/give/giving/
>
> I'm slightly worried that due to using --no-overlay mode by default in
> restore, having tab-completion include untracked files increases the
> risk of accidentally nuking the wrong file.  restore is a destructive
> command anyway and should thus be used with care, so perhaps this
> isn't a big deal, but I thought I'd mention it.

This makes me think about my "git restore :/" example, which does not
remove untracked files because its source is the index. But isn't it
inconsistent that we only remove untracked files with --source and
keep them without? Will that just confuse people more? Or did I just
forget to implement no-overlay mode for the index too?

Side note, to enable overlay mode again in git restore, maybe "git
restore --keep-untracked --source..." will be better than "git restore
--overlay --source..."
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 10/11] completion: support restore
  2019-03-11 15:22     ` Duy Nguyen
@ 2019-03-11 15:39       ` Duy Nguyen
  2019-03-11 18:28         ` Elijah Newren
  0 siblings, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-11 15:39 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano

On Mon, Mar 11, 2019 at 10:22 PM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:
> >
> > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > >
> > > Completion for restore is straightforward. We could still do better
> > > though by give the list of just tracked files instead of all present
> > > ones. But let's leave it for later.
> >
> > s/give/giving/
> >
> > I'm slightly worried that due to using --no-overlay mode by default in
> > restore, having tab-completion include untracked files increases the
> > risk of accidentally nuking the wrong file.  restore is a destructive
> > command anyway and should thus be used with care, so perhaps this
> > isn't a big deal, but I thought I'd mention it.
>
> This makes me think about my "git restore :/" example, which does not
> remove untracked files because its source is the index. But isn't it
> inconsistent that we only remove untracked files with --source and
> keep them without? Will that just confuse people more? Or did I just
> forget to implement no-overlay mode for the index too?

Nope you confused me. non-overlay mode never touches untracked files
and so neither does git-restore.

It does make the description of git-restore about "remove if paths do
not exist" incorrect. But frankly I find it hard to explain
non-overlay mode with the index being the remove filter. In
git-checkout, where we update both index and worktree, this may make
sense to use the index as the remove filter. But worktree works on the
worktree only by default. I'll need to sleep on this.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 00/11] And new command "restore"
  2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
  2019-03-10 22:45   ` Jacob Keller
@ 2019-03-11 16:01   ` Elijah Newren
  2019-03-13 22:58   ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-11 16:01 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, Andrei Rybak

On Sun, Mar 10, 2019 at 4:19 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > - --index has a different meaning in git-apply. I don't have a good
> >   suggestion except "yeah we have two different worlds now, the old
> >   and the new one"
>
> I will rename --index to --staged to avoid this. It is already used as
> synonym for --cached in git-diff. I will also add --staged as synonym
> to "git rm" so that "git status" can consistently advise "git <verb>
> --staged" where verb is either restore or rm.
>
> Since option rename is a lot of work. If you think this is not a good
> move, raise it now, even if it's just "I don't like it, no reasons".
> --

Works for me, but this sounds like the kind of thing Junio might
object to, so I'm curious about his input.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 10/11] completion: support restore
  2019-03-11 15:39       ` Duy Nguyen
@ 2019-03-11 18:28         ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-11 18:28 UTC (permalink / raw)
  To: Duy Nguyen, Thomas Gummerer; +Cc: Git Mailing List, Junio C Hamano

// CC'ing Thomas since this touches on a thread elsewhere about the glossary

On Mon, Mar 11, 2019 at 8:40 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Mon, Mar 11, 2019 at 10:22 PM Duy Nguyen <pclouds@gmail.com> wrote:
> >
> > On Sun, Mar 10, 2019 at 2:17 AM Elijah Newren <newren@gmail.com> wrote:
> > >
> > > On Fri, Mar 8, 2019 at 2:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > > >
> > > > Completion for restore is straightforward. We could still do better
> > > > though by give the list of just tracked files instead of all present
> > > > ones. But let's leave it for later.
> > >
> > > s/give/giving/
> > >
> > > I'm slightly worried that due to using --no-overlay mode by default in
> > > restore, having tab-completion include untracked files increases the
> > > risk of accidentally nuking the wrong file.  restore is a destructive
> > > command anyway and should thus be used with care, so perhaps this
> > > isn't a big deal, but I thought I'd mention it.
> >
> > This makes me think about my "git restore :/" example, which does not
> > remove untracked files because its source is the index. But isn't it
> > inconsistent that we only remove untracked files with --source and
> > keep them without? Will that just confuse people more? Or did I just
> > forget to implement no-overlay mode for the index too?
>
> Nope you confused me. non-overlay mode never touches untracked files
> and so neither does git-restore.
>
> It does make the description of git-restore about "remove if paths do
> not exist" incorrect. But frankly I find it hard to explain
> non-overlay mode with the index being the remove filter. In
> git-checkout, where we update both index and worktree, this may make
> sense to use the index as the remove filter. But worktree works on the
> worktree only by default. I'll need to sleep on this.

So, I guess this means that my addendum to Thomas' glossary entry for
overlay is wrong; instead of:

[[def_overlay]]overlay::
       Only update and add files to the working directory, but don't
       delete them, similar to how 'cp -R' works.  This is the default
       in a <<def_checkout,checkout>>.  In contrast, no-overlay mode
       will also delete files not present in the source, similar to
       'rsync --delete'.

the definition should be (note the insertion of 'tracked'):

[[def_overlay]]overlay::
       Only update and add files to the working directory, but don't
       delete them, similar to how 'cp -R' works.  This is the default
       in a <<def_checkout,checkout>>.  In contrast, no-overlay mode
       will also delete tracked files not present in the source,
       similar to 'rsync --delete'.

...and perhaps the reason this makes it hard for you to explain when
the index is the source is because this definition means there is no
difference between overlay and non-overlay mode for that specific
case.


Am I on the right page now?

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 11/11] doc: promote "git restore"
  2019-03-11 14:36       ` Duy Nguyen
@ 2019-03-13  4:58         ` Junio C Hamano
  2019-04-11 10:55           ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Junio C Hamano @ 2019-03-13  4:58 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Randall S. Becker, Elijah Newren, Git Mailing List

Duy Nguyen <pclouds@gmail.com> writes:

>> 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.
>
> I declare "git checkout" not commonly used the moment git-switch and
> git-restore are released!
>
> More seriously, this is an attempt to promote new commands. The
> "commonly used" here refers to the set of commands that show when you
> run "git help" (they are called "common" in command-list.txt
> classification). Since the people who type "git help" are mostly new
> people, I think promoting the friendlier alternative is a good idea.

So it is mainly how the word "common" is defined?  It's not how
common the command is in the minds of the population of Git users,
but how common we want it to be?

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
  2019-03-09 16:53   ` Andrei Rybak
@ 2019-03-13  9:13   ` Johannes Schindelin
  2019-03-13  9:20     ` Duy Nguyen
  2019-03-14  5:45   ` Junio C Hamano
  2 siblings, 1 reply; 97+ messages in thread
From: Johannes Schindelin @ 2019-03-13  9:13 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano, Elijah Newren

[-- Attachment #1: Type: text/plain, Size: 1904 bytes --]

Hi Duy,

On Fri, 8 Mar 2019, Nguyễn Thái Ngọc Duy wrote:

> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
> new file mode 100755
> index 0000000000..df91bf54bc
> --- /dev/null
> +++ b/t/t2070-restore.sh
> @@ -0,0 +1,77 @@
> +#!/bin/sh
> +
> +test_description='restore basic functionality'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> +	test_commit first &&
> +	echo first-and-a-half >>first.t &&
> +	git add first.t &&
> +	test_commit second &&
> +	echo one >one &&
> +	echo two >two &&
> +	echo untracked >untracked &&
> +	echo ignored >ignored &&
> +	echo /ignored >.gitignore &&
> +	git add one two .gitignore &&
> +	git update-ref refs/heads/one master
> +'
> +
> +test_expect_success 'restore without pathspec is not ok' '
> +	test_must_fail git restore &&
> +	test_must_fail git restore --source=first
> +'
> +
> +test_expect_success 'restore -p without pathspec is fine' '
> +	echo q >cmd &&
> +	git restore -p <cmd
> +'

This breaks with NO_PERL builds. See e.g.

https://dev.azure.com/gitgitgadget/git/_build/results?buildId=4581
https://dev.azure.com/gitgitgadget/git/_build/results?buildId=4584
https://dev.azure.com/git/git/_build/results?buildId=386

You need this squashed in:

diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index df91bf54bc06..f4766544c5de 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -23,7 +23,7 @@ test_expect_success 'restore without pathspec is not ok'
'
 	test_must_fail git restore --source=first
 '
 
-test_expect_success 'restore -p without pathspec is fine' '
+test_expect_success PERL 'restore -p without pathspec is fine' '
 	echo q >cmd &&
 	git restore -p <cmd
 '


Junio, could you please add that as SQUASH??? on top of
nd/switch-and-restore to unbreak the CI builds?

Duy, have you thought about making use of the CI builds? You could catch
those bugs before they hit the Git mailing list...

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
  2019-03-08 18:01   ` Elijah Newren
@ 2019-03-13  9:17   ` Johannes Schindelin
  1 sibling, 0 replies; 97+ messages in thread
From: Johannes Schindelin @ 2019-03-13  9:17 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano, Elijah Newren

[-- Attachment #1: Type: text/plain, Size: 1135 bytes --]

Hi Duy,

On Fri, 8 Mar 2019, Nguyễn Thái Ngọc Duy wrote:

> diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
> new file mode 100644
> index 0000000000..a667a5ced4
> --- /dev/null
> +++ b/Documentation/git-restore.txt
> @@ -0,0 +1,172 @@
> [...]
> +
> +To restore all files in the current directory
> +
> +------------
> +$ git restore .
> +------------
> +
> +or to restore all working tree files with 'top' pathspec magic (see
> +linkgit::gitglossary[7])

This has an extra colon, breaking the build:

https://dev.azure.com/git/git/_build/results?buildId=386

You need this squashed in:

diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index a667a5ced4ec..1faeee50593e 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -144,7 +144,7 @@ $ git restore .
 ------------
 
 or to restore all working tree files with 'top' pathspec magic (see
-linkgit::gitglossary[7])
+linkgit:gitglossary[7])
 
 ------------
 $ git restore :/

Junio, could you please put this as a SQUASH??? on top of
nd/switch-and-restore, to unbreak the CI builds?

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-13  9:13   ` Johannes Schindelin
@ 2019-03-13  9:20     ` Duy Nguyen
  2019-03-13 22:17       ` Johannes Schindelin
  0 siblings, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-13  9:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Junio C Hamano, Elijah Newren

On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> Duy, have you thought about making use of the CI builds? You could catch
> those bugs before they hit the Git mailing list...

Using Azure? No.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 06/11] restore: add --worktree and --index
  2019-03-09 18:52   ` Elijah Newren
  2019-03-10 20:03     ` Eric Sunshine
@ 2019-03-13 10:02     ` Duy Nguyen
  2019-03-13 22:42     ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-03-13 10:02 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano

On Sun, Mar 10, 2019 at 1:52 AM Elijah Newren <newren@gmail.com> wrote:
> > +               /*
> > +                * NEEDSWORK: if --worktree is not specified, we
> > +                * should save stat info of checked out files in the
> > +                * index to avoid the next (potentially costly)
> > +                * refresh. But it's a bit tricker to do...
> > +                */
> > +               rollback_lock_file(&lock_file);
>
> A total tangent: I see both FIXME and NEEDSWORK in the codebase.  Are
> there other 'keywords' of this type that we use?  Is there a
> preference for how they are used?

I don't think so. I've seen FIXME, NEEDSWORK, TODO and XXX. I think
it's often up to the author to pick one. We could unify and use just
one keyword, which helps spot these. But I don't think we keep future
other keywords out long term. This seems to pick up most of them with
a couple false positives

git grep '^\s*\* [A-Z]\+: ' \*.c
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-13  9:20     ` Duy Nguyen
@ 2019-03-13 22:17       ` Johannes Schindelin
  2019-03-14  4:57         ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Johannes Schindelin @ 2019-03-13 22:17 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Junio C Hamano, Elijah Newren

Hi Duy,

On Wed, 13 Mar 2019, Duy Nguyen wrote:

> On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
> > Duy, have you thought about making use of the CI builds? You could catch
> > those bugs before they hit the Git mailing list...
> 
> Using Azure? No.

This hostility is totally unnecessary. Especially since we still have
Travis builds that you could also use to catch your bugs early, so that I
would not have to catch them.

You could also work on setting up Circle CI, or whatever else you have in
mind. As long as you catch your bugs early.

Thanks,
Johannes

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 06/11] restore: add --worktree and --index
  2019-03-09 18:52   ` Elijah Newren
  2019-03-10 20:03     ` Eric Sunshine
  2019-03-13 10:02     ` Duy Nguyen
@ 2019-03-13 22:42     ` Junio C Hamano
  2 siblings, 0 replies; 97+ messages in thread
From: Junio C Hamano @ 2019-03-13 22:42 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Nguyễn Thái Ngọc Duy, Git Mailing List

Elijah Newren <newren@gmail.com> writes:

>> +               /*
>> +                * NEEDSWORK: if --worktree is not specified, we
>> +                * should save stat info of checked out files in the
>> +                * index to avoid the next (potentially costly)
>> +                * refresh. But it's a bit tricker to do...
>> +                */
>> +               rollback_lock_file(&lock_file);
>
> A total tangent: I see both FIXME and NEEDSWORK in the codebase.
> Are there other 'keywords' of this type that we use?  Is there a
> preference for how they are used?

If it makes it simpler, I can easily declare that NEEDSWORK is the
preferred one (I do not think I ever wrote anything else) to avoid
wasting list bandwidth ;-)


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 00/11] And new command "restore"
  2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
  2019-03-10 22:45   ` Jacob Keller
  2019-03-11 16:01   ` Elijah Newren
@ 2019-03-13 22:58   ` Junio C Hamano
  2019-03-14  3:49     ` Duy Nguyen
  2 siblings, 1 reply; 97+ messages in thread
From: Junio C Hamano @ 2019-03-13 22:58 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Elijah Newren, Eric Sunshine, Andrei Rybak

Duy Nguyen <pclouds@gmail.com> writes:

> On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> - --index has a different meaning in git-apply. I don't have a good
>>   suggestion except "yeah we have two different worlds now, the old
>>   and the new one"
>
> I will rename --index to --staged to avoid this. It is already used as
> synonym for --cached in git-diff. I will also add --staged as synonym
> to "git rm" so that "git status" can consistently advise "git <verb>
> --staged" where verb is either restore or rm.

Does "restore --index" (now "--staged") work the same way as
"--cached" aka "--staged" in git-diff?  "--cached" is about
affecting only what is in the index (as opposed to "--index"
affecting both the index and the working tree).

If it does, then calling it "--cached" aka "--staged" does make more
sense than calling it "--index", I would think.  Otherwise, such a
rename does not buy us much.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 00/11] And new command "restore"
  2019-03-13 22:58   ` Junio C Hamano
@ 2019-03-14  3:49     ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-03-14  3:49 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Elijah Newren, Eric Sunshine, Andrei Rybak

On Thu, Mar 14, 2019 at 5:58 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> > On Fri, Mar 8, 2019 at 5:17 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> >> - --index has a different meaning in git-apply. I don't have a good
> >>   suggestion except "yeah we have two different worlds now, the old
> >>   and the new one"
> >
> > I will rename --index to --staged to avoid this. It is already used as
> > synonym for --cached in git-diff. I will also add --staged as synonym
> > to "git rm" so that "git status" can consistently advise "git <verb>
> > --staged" where verb is either restore or rm.
>
> Does "restore --index" (now "--staged") work the same way as
> "--cached" aka "--staged" in git-diff?  "--cached" is about
> affecting only what is in the index (as opposed to "--index"
> affecting both the index and the working tree).

Yes. To affect both you would need --staged --worktree (or -WS for
short because I don't think anybody is going to type that many
characters)

> If it does, then calling it "--cached" aka "--staged" does make more
> sense than calling it "--index", I would think.  Otherwise, such a
> rename does not buy us much.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-13 22:17       ` Johannes Schindelin
@ 2019-03-14  4:57         ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-03-14  4:57 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Junio C Hamano, Elijah Newren

On Thu, Mar 14, 2019 at 5:17 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> Hi Duy,
>
> On Wed, 13 Mar 2019, Duy Nguyen wrote:
>
> > On Wed, Mar 13, 2019 at 4:13 PM Johannes Schindelin
> > <Johannes.Schindelin@gmx.de> wrote:
> > > Duy, have you thought about making use of the CI builds? You could catch
> > > those bugs before they hit the Git mailing list...
> >
> > Using Azure? No.
>
> This hostility is totally unnecessary. Especially since we still have
> Travis builds that you could also use to catch your bugs early, so that I
> would not have to catch them.

Thanks. I'll check back on github in a couple years. Adding CI to
gitlab is on my todo list but it's very low.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
  2019-03-09 16:53   ` Andrei Rybak
  2019-03-13  9:13   ` Johannes Schindelin
@ 2019-03-14  5:45   ` Junio C Hamano
  2019-03-14  5:55     ` Junio C Hamano
  2 siblings, 1 reply; 97+ messages in thread
From: Junio C Hamano @ 2019-03-14  5:45 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Elijah Newren

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
> new file mode 100755
> index 0000000000..df91bf54bc
> --- /dev/null
> +++ b/t/t2070-restore.sh
> @@ -0,0 +1,77 @@
> +#!/bin/sh
> +
> +test_description='restore basic functionality'
> +
> +. ./test-lib.sh
> + ...
> +test_expect_success 'restore -p without pathspec is fine' '
> +	echo q >cmd &&
> +	git restore -p <cmd
> +'

This stands out as a sore thumb, being an invocation with '-p' while
all the other tests for the '-p' feature are in t2071.

Shoudln't it move to somewhere near the beginning of 2071?

> diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
> new file mode 100755
> index 0000000000..46ebcb2413
> --- /dev/null
> +++ b/t/t2071-restore-patch.sh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 09/11] t: add tests for restore
  2019-03-14  5:45   ` Junio C Hamano
@ 2019-03-14  5:55     ` Junio C Hamano
  0 siblings, 0 replies; 97+ messages in thread
From: Junio C Hamano @ 2019-03-14  5:55 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Elijah Newren

Junio C Hamano <gitster@pobox.com> writes:

>> +test_expect_success 'restore -p without pathspec is fine' '
>> +	echo q >cmd &&
>> +	git restore -p <cmd
>> +'
>
> This stands out as a sore thumb, being an invocation with '-p' while
> all the other tests for the '-p' feature are in t2071.

I'll have this inserted immediately after 09/11 in the meantime.

I wonder if there is a clean way to make everything in 2071 with
PERL prereq.  I generally do not like the pattern used by some tests
that manually check the prererq and have test_done early, though.

Thanks.

-- >8 --
From: Junio C Hamano <gitster@pobox.com>
Date: Thu, 14 Mar 2019 14:48:47 +0900
Subject: [PATCH] SQUASH??? move -p test to 2071 from 2070

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t2070-restore.sh       | 5 -----
 t/t2071-restore-patch.sh | 5 +++++
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index df91bf54bc..3dc7032839 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -23,11 +23,6 @@ test_expect_success 'restore without pathspec is not ok' '
 	test_must_fail git restore --source=first
 '
 
-test_expect_success 'restore -p without pathspec is fine' '
-	echo q >cmd &&
-	git restore -p <cmd
-'
-
 test_expect_success 'restore a file, ignoring branch of same name' '
 	cat one >expected &&
 	echo dirty >>one &&
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index 46ebcb2413..98b2476e7c 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -16,6 +16,11 @@ test_expect_success PERL 'setup' '
 	save_head
 '
 
+test_expect_success PERL 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
 # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
 
 test_expect_success PERL 'saying "n" does nothing' '
-- 
2.21.0-155-ge902e9bcae



^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-08 18:01   ` Elijah Newren
  2019-03-09 12:16     ` Duy Nguyen
@ 2019-03-25  9:53     ` Duy Nguyen
  2019-03-25 15:51       ` Elijah Newren
  1 sibling, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-03-25  9:53 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano, Stefan Beller

(Including Stefan, in case he's still interested in git and planned
something for the "submodule" restore part I mention below)

On Fri, Mar 08, 2019 at 10:01:25AM -0800, Elijah Newren wrote:
> Thanks for working on this; overall looks really good.  I've got a few
> comments here and there on the wording and combinations of options...

OK now that the 'git switch' part is mostly settled, I could go back
to 'git restore'. I have a lot more homework to do before sending v2,
but how about this?

Most of this is minor rephrasing and clarification (thanks!). The big
change is `--force` is split into `--keep-unmerged` and
`--force-submodules`, the same direction I took in 'git switch'.

Submodules actually pose another problem because there are different
things in a submodule to restore:

 - restore HEAD only, leave submodule's worktree alone. This is
   `--no-recurse-submodules` (default)
 
 - restoring `HEAD`, which is basically "git switch --detach
   <something>" inside the submodule. This is `--recurse-submodules`.

 - restoring the paths inside the submodules, i.e. the actual "git
   restore <some paths>" inside the submodule.

 - a combinarion of two. Basically you want "git reset --hard" in
   every selected submodule.

I wonder if we ever need to cover the third and forth case (and if so,
what will the UI be like).  I suppose we could eventually make
--recurse-submodules take an argument to specify what to restore,
maybe...

PS. I just found https://asciidoclive.com/. Could be useful to paste
this text and render without going through git's build system.

-- 8< --
git-restore(1)
==============

NAME
----
git-restore - Restore working tree files

SYNOPSIS
--------
[verse]
'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]

DESCRIPTION
-----------
Restore specified paths in the working tree with some contents from a
restore source. If a path is tracked but does not exist in the restore
source, it will be removed to match the source.

The command can also be used to restore the content in the index with
`--staged`, or restore both the working tree and the index with
`--staged --worktree`.

By default, the restore sources for working tree and the index are the
index and `HEAD` respectively. `--source` could be used to specify a
commit as the restore source.

A note about the differences between this command and linkgit:git-reset[1].
`git restore` modifies the working tree (and maybe the index) to
change file content to match some other, usually older, version, but
does not update your branch. `git reset` is mainly about moving the
tip of your branch in order to either add or remove commits from the
branch.

OPTIONS
-------
-s <tree>::
--source=<tree>::
	Restore the working tree files with the content from the given
	tree. It is common to specify the source tree by naming a
	commit, branch or tag associated with it.
+
If not specified, the default restore source for the working tree is
the index, and index `HEAD`. When both `--staged` and `--worktree`
are specified, `--source` must also be specified.

-p::
--patch::
	Interactively select hunks in the difference between the
	restore source and the restore location. See the ``Interactive
	Mode'' section of linkgit:git-add[1] to learn how to operate
	the `--patch` mode.
+
Note that `--patch` can accept no pathspec and will restore all
modified paths.

-W::
--worktree::
-S::
--staged::
	Specify the restore location. If neither option is specified,
	by default the working tree is restored. Specifying `--staged`
	will only restore the index. Specifying both restores both.

-q::
--quiet::
	Quiet, suppress feedback messages.

--progress::
--no-progress::
	Progress status is reported on the standard error stream
	by default when it is attached to a terminal, unless `--quiet`
	is specified. This flag enables progress reporting even if not
	attached to a terminal, regardless of `--quiet`.

-f::
--force::
	An alias of `--keep-unmerged` and `--force-submodules`.

--keep-unmerged::
	If `--source` is not specified, unmerged entries are left
	alone and will not fail the operation.

--force-submodules::
	Continue even if restoring `HEAD` of a submodule leads to loss
	of local changes in that submodule with `--recurse-submodules`.

--ours::
--theirs::
	Restore from stage #2 ('ours') or #3 ('theirs') for unmerged
	paths.
+
Note that during `git rebase` and `git pull --rebase`, 'ours' and
'theirs' may appear swapped. See the explanation of the same options
in linkgit:git-checkout[1] for details.

-m::
--merge::
	Recreate the conflicted merge in the specified paths.

--conflict=<style>::
	The same as `--merge` option above, but changes the way the
	conflicting hunks are presented, overriding the merge.conflictStyle
	configuration variable.  Possible values are "merge" (default)
	and "diff3" (in addition to what is shown by "merge" style,
	shows the original contents).

--ignore-skip-worktree-bits::
	In sparse checkout mode, by default is to only update entries
	matched by `<pathspec>` and sparse patterns in
	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
	patterns and unconditionally restores any files in
	`<pathspec>`.

--recurse-submodules::
--no-recurse-submodules::
	Using `--recurse-submodules` will update the content of all
	initialized submodules according to the commit recorded in the
	superproject. If nothing (or `--no-recurse-submodules`) is
	used, the work trees of submodules will not be updated.  Just
	like linkgit:git-submodule[1], this will detach `HEAD` of the
	submodules.

--overlay::
--no-overlay::
	In overlay mode, the command never removes files when
	restoring. In no-overlay mode, tracked files that appear in
	the index and working tree, but not in `--source` tree are
	removed, to make them match `<tree>` exactly. The default is
	no-overlay mode.

EXAMPLES
--------

The following sequence switches to the `master` branch, reverts the
`Makefile` to two revisions back, deletes hello.c by mistake, and gets
it back from the index.

------------
$ git switch master
$ git restore --source master~2 Makefile  <1>
$ rm -f hello.c
$ git restore hello.c                     <2>
------------

<1> take a file out of another commit
<2> restore hello.c from the index

If you want to restore _all_ C source files to match the version in
the index, you can say

------------
$ git restore '*.c'
------------

Note the quotes around `*.c`.  The file `hello.c` will also be
restored, even though it is no longer in the working tree, because the
file globbing is used to match entries in the index (not in the
working tree by the shell).

To restore all files in the current directory

------------
$ git restore .
------------

or to restore all working tree files with 'top' pathspec magic (see
linkgit:gitglossary[7])

------------
$ git restore :/
------------

To restore a file in the index to match the version in `HEAD` (this is
the same as using linkgit:git-reset[1])

------------
$ git restore --staged hello.c
------------

or you can restore both the index and the working tree (this the same
as using linkgit:git-checkout[1])

------------
$ git restore --source=HEAD --staged --worktree hello.c
------------

or the short form which is more practical but less readable:

------------
$ git restore -s@ -SW hello.c
------------

SEE ALSO
--------
linkgit:git-checkout[1],
linkgit:git-reset[1]

GIT
---
Part of the linkgit:git[1] suite
-- 8< --

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-25  9:53     ` Duy Nguyen
@ 2019-03-25 15:51       ` Elijah Newren
  0 siblings, 0 replies; 97+ messages in thread
From: Elijah Newren @ 2019-03-25 15:51 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Junio C Hamano, Stefan Beller

On Mon, Mar 25, 2019 at 2:53 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> (Including Stefan, in case he's still interested in git and planned
> something for the "submodule" restore part I mention below)
>
> On Fri, Mar 08, 2019 at 10:01:25AM -0800, Elijah Newren wrote:
> > Thanks for working on this; overall looks really good.  I've got a few
> > comments here and there on the wording and combinations of options...
>
> OK now that the 'git switch' part is mostly settled, I could go back
> to 'git restore'. I have a lot more homework to do before sending v2,
> but how about this?
>
> Most of this is minor rephrasing and clarification (thanks!). The big
> change is `--force` is split into `--keep-unmerged` and
> `--force-submodules`, the same direction I took in 'git switch'.
>
> Submodules actually pose another problem because there are different
> things in a submodule to restore:
>
>  - restore HEAD only, leave submodule's worktree alone. This is
>    `--no-recurse-submodules` (default)
>
>  - restoring `HEAD`, which is basically "git switch --detach
>    <something>" inside the submodule. This is `--recurse-submodules`.
>
>  - restoring the paths inside the submodules, i.e. the actual "git
>    restore <some paths>" inside the submodule.
>
>  - a combinarion of two. Basically you want "git reset --hard" in
>    every selected submodule.
>
> I wonder if we ever need to cover the third and forth case (and if so,
> what will the UI be like).  I suppose we could eventually make
> --recurse-submodules take an argument to specify what to restore,
> maybe...

This looks pretty good to me, though I've got some small comments
below.  Note that I'm basically glossing over or ignoring any part
touching on submodules, so I could easily miss even glaring wording
problems in that part.

> PS. I just found https://asciidoclive.com/. Could be useful to paste
> this text and render without going through git's build system.

Interesting.  :-)

> -- 8< --
> git-restore(1)
> ==============
>
> NAME
> ----
> git-restore - Restore working tree files
>
> SYNOPSIS
> --------
> [verse]
> 'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
> 'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
>
> DESCRIPTION
> -----------
> Restore specified paths in the working tree with some contents from a
> restore source. If a path is tracked but does not exist in the restore
> source, it will be removed to match the source.
>
> The command can also be used to restore the content in the index with
> `--staged`, or restore both the working tree and the index with
> `--staged --worktree`.
>
> By default, the restore sources for working tree and the index are the
> index and `HEAD` respectively. `--source` could be used to specify a
> commit as the restore source.
>
> A note about the differences between this command and linkgit:git-reset[1].
> `git restore` modifies the working tree (and maybe the index) to
> change file content to match some other, usually older, version, but
> does not update your branch. `git reset` is mainly about moving the
> tip of your branch in order to either add or remove commits from the
> branch.

Maybe include revert as well?  Perhaps something like...

A note about the differences between this command, linkgit:git-reset[1],
and and linkgit:git-revert[1].  `git restore` modifies the working tree
(and maybe the index) to change file content to match some other,
usually older, version, but does not update your branch. `git reset` is
mainly about moving the tip of your branch to some other commit,
implicitly adding and/or removing many commits to the branch.  `git
revert` is about creating and adding new commits to your branch that
reverse the changes made in specified commits.

(There is also a similar description in git-revert, which also
compares all three of revert, reset, and checkout.  It's shorter, but
focuses more from the "revert" angle.)

> OPTIONS
> -------
> -s <tree>::
> --source=<tree>::
>         Restore the working tree files with the content from the given
>         tree. It is common to specify the source tree by naming a
>         commit, branch or tag associated with it.
> +
> If not specified, the default restore source for the working tree is
> the index, and index `HEAD`. When both `--staged` and `--worktree`
> are specified, `--source` must also be specified.

s/index `HEAD`/the default restore source for the index is `HEAD`/

> -p::
> --patch::
>         Interactively select hunks in the difference between the
>         restore source and the restore location. See the ``Interactive
>         Mode'' section of linkgit:git-add[1] to learn how to operate
>         the `--patch` mode.
> +
> Note that `--patch` can accept no pathspec and will restore all
> modified paths.

s/will restore all/will prompt to restore with all/

> -W::
> --worktree::
> -S::
> --staged::
>         Specify the restore location. If neither option is specified,
>         by default the working tree is restored. Specifying `--staged`
>         will only restore the index. Specifying both restores both.
>
> -q::
> --quiet::
>         Quiet, suppress feedback messages.
>
> --progress::
> --no-progress::
>         Progress status is reported on the standard error stream
>         by default when it is attached to a terminal, unless `--quiet`
>         is specified. This flag enables progress reporting even if not
>         attached to a terminal, regardless of `--quiet`.
>
> -f::
> --force::
>         An alias of `--keep-unmerged` and `--force-submodules`.
>
> --keep-unmerged::
>         If `--source` is not specified, unmerged entries are left
>         alone and will not fail the operation.

This isn't very clear to me.  Maybe:

Incompatible with --source and --staged.  When specified, unmerged
entries in the index will not fail the operation and the corresponding
working tree files will be left unmodified.

> --force-submodules::
>         Continue even if restoring `HEAD` of a submodule leads to loss
>         of local changes in that submodule with `--recurse-submodules`.
>
> --ours::
> --theirs::
>         Restore from stage #2 ('ours') or #3 ('theirs') for unmerged
>         paths.
> +
> Note that during `git rebase` and `git pull --rebase`, 'ours' and
> 'theirs' may appear swapped. See the explanation of the same options
> in linkgit:git-checkout[1] for details.
>
> -m::
> --merge::
>         Recreate the conflicted merge in the specified paths.
>
> --conflict=<style>::
>         The same as `--merge` option above, but changes the way the
>         conflicting hunks are presented, overriding the merge.conflictStyle
>         configuration variable.  Possible values are "merge" (default)
>         and "diff3" (in addition to what is shown by "merge" style,
>         shows the original contents).
>
> --ignore-skip-worktree-bits::
>         In sparse checkout mode, by default is to only update entries
>         matched by `<pathspec>` and sparse patterns in
>         $GIT_DIR/info/sparse-checkout. This option ignores the sparse
>         patterns and unconditionally restores any files in
>         `<pathspec>`.
>
> --recurse-submodules::
> --no-recurse-submodules::
>         Using `--recurse-submodules` will update the content of all
>         initialized submodules according to the commit recorded in the
>         superproject. If nothing (or `--no-recurse-submodules`) is
>         used, the work trees of submodules will not be updated.  Just
>         like linkgit:git-submodule[1], this will detach `HEAD` of the
>         submodules.
>
> --overlay::
> --no-overlay::
>         In overlay mode, the command never removes files when
>         restoring. In no-overlay mode, tracked files that appear in
>         the index and working tree, but not in `--source` tree are
>         removed, to make them match `<tree>` exactly. The default is
>         no-overlay mode.

I think the "index and working tree" wording might cause some
problems.  What if the path doesn't exist in the source tree nor the
working tree, but does appear in the index and the user passes
--staged?  Maybe just remove that phrase, e.g.:

"...In no-overlay mode, tracked files that do not appear in the
`--source` tree are removed..."

?

> EXAMPLES
> --------
>
> The following sequence switches to the `master` branch, reverts the
> `Makefile` to two revisions back, deletes hello.c by mistake, and gets
> it back from the index.
>
> ------------
> $ git switch master
> $ git restore --source master~2 Makefile  <1>
> $ rm -f hello.c
> $ git restore hello.c                     <2>
> ------------
>
> <1> take a file out of another commit
> <2> restore hello.c from the index
>
> If you want to restore _all_ C source files to match the version in
> the index, you can say
>
> ------------
> $ git restore '*.c'
> ------------
>
> Note the quotes around `*.c`.  The file `hello.c` will also be
> restored, even though it is no longer in the working tree, because the
> file globbing is used to match entries in the index (not in the
> working tree by the shell).
>
> To restore all files in the current directory
>
> ------------
> $ git restore .
> ------------
>
> or to restore all working tree files with 'top' pathspec magic (see
> linkgit:gitglossary[7])
>
> ------------
> $ git restore :/
> ------------
>
> To restore a file in the index to match the version in `HEAD` (this is
> the same as using linkgit:git-reset[1])
>
> ------------
> $ git restore --staged hello.c
> ------------
>
> or you can restore both the index and the working tree (this the same
> as using linkgit:git-checkout[1])
>
> ------------
> $ git restore --source=HEAD --staged --worktree hello.c
> ------------
>
> or the short form which is more practical but less readable:
>
> ------------
> $ git restore -s@ -SW hello.c
> ------------

@ means HEAD?  I somehow missed this for five and half years.  (Glad
you put the "less readable" disclaimer/warning on this example,
though.)

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 01/11] checkout: split part of it to new command 'restore'
  2019-03-09 18:27       ` Elijah Newren
@ 2019-04-09 12:18         ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-04-09 12:18 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Git Mailing List, Junio C Hamano

(since 'git switch' is basically done, let's get back to 'git restore')

On Sun, Mar 10, 2019 at 1:27 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Sat, Mar 9, 2019 at 4:16 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > On Sat, Mar 9, 2019 at 1:01 AM Elijah Newren <newren@gmail.com> wrote:
>
> > > > +-q::
> > > > +--quiet::
> > > > +       Quiet, suppress feedback messages.
> > > > +
> > > > +--progress::
> > > > +--no-progress::
> > > > +       Progress status is reported on the standard error stream
> > > > +       by default when it is attached to a terminal, unless `--quiet`
> > > > +       is specified. This flag enables progress reporting even if not
> > > > +       attached to a terminal, regardless of `--quiet`.
> > >
> > > I'm assuming this means there are feedback messages other than
> > > progress feedback?
> >
> > There could be. This is carried over from git-checkout. I suspect this
> > is about warnings that we print from time to time.
>
> Why would --quiet squelch warnings?  I figured it'd only squelch
> feedback, informational, or progress messages.

I'm wrong. Warnings are not suppressed by --quiet. It looks like
--quiet is not really used by 'git restore'. Based on code inspection,
--quiet implies --no-progress if --[no-]progress is not specified.
Otherwise progress bar is enabled only when we have tty.

> I understand you just carried it over from git-checkout, but as worded
> it makes me wonder if checkout has suboptimal behavior or perhaps just
> a suboptimal explanation of its flags ... and if it does, we probably
> don't want to carry that over.

But there is something we don't show now, but I don't know if we should show.

The commit that started all this is 0f086e6dca (checkout: print
something when checking out paths, 2018-11-13) where I tried to make
'git checkout' mistakes easier to see by printing

    Updated <n> paths from <some-checkout-source>

Because the reason is to help spot mistakes, this is only printed when
you do "git checkout <paths>" _without_ the double dashes to
disambiguate.

Since git-restore has no ambiguation (between restoring paths and
switching branches) these messages are never printed. I'm just
wondering if we should print them anyway. It gives some feedback
instead of the silent output in the successful case. Some might like
it, some don't.

> > > > +-f::
> > > > +--force::
> > > > +       If `--source` is not specified, unmerged entries are left alone
> > > > +       and will not fail the operation. Unmerged entries are always
> > > > +       replaced if `--source` is specified, regardless of `--force`.
> > >
> > > This may be slightly confusing, in particular it suggests that --index
> > > (or --worktree and --index) are the default.  Is --force only useful
> > > when --index is specified?  If it has utility with --worktree only,
> > > what does it do?
> >
> > Well, this is 'git checkout -f' behavior which only concerns the
> > index. So yeah it only matters with --index.
>
> Okay, good to know that this only matters with --index. However, new
> problem: This makes the explanation feel contradictory, though,
> because elsewhere you stated that --source=HEAD is implied when
> --index is given without a source.  So, the combination of this
> description and that fact suggests that -f is either useless (--index
> is not specified) or ignored (because --source will either default to
> HEAD or be specified by the user).  Maybe that's true and -f should be
> removed from this new document.  If it has actual utility in some
> cases, then this description needs to be reworked to explain what
> those circiumstances are somehow.

Actually my description of this option is not clear. This is about
--worktree, not --index.

When we do "git restore <path>" (which by default implies --worktree),
we take the entry from the index and overwrite the worktree with it.
But if the entry is unmerged (and --ours or --theirs are not
specified), there is no good way we can handle it automatically.

So the default behavior in this case is abort the operation. -f goes
with a gentler option: ignore unmerged entries and go restore normal
ones. And to keep people informed that there _are_ unmerged entries,
warning is displayed for each one.

So, maybe I'll rename --force to --ignore-unmerged? This option will
only matter in the "git restore [--worktree]" form. In other words,
restoring worktree from the index. If --ignored-unmerged is specified
in other modes, die().
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v1 11/11] doc: promote "git restore"
  2019-03-13  4:58         ` Junio C Hamano
@ 2019-04-11 10:55           ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-04-11 10:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Randall S. Becker, Elijah Newren, Git Mailing List

On Wed, Mar 13, 2019 at 11:58 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> >> 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.
> >
> > I declare "git checkout" not commonly used the moment git-switch and
> > git-restore are released!
> >
> > More seriously, this is an attempt to promote new commands. The
> > "commonly used" here refers to the set of commands that show when you
> > run "git help" (they are called "common" in command-list.txt
> > classification). Since the people who type "git help" are mostly new
> > people, I think promoting the friendlier alternative is a good idea.
>
> So it is mainly how the word "common" is defined?  It's not how
> common the command is in the minds of the population of Git users,
> but how common we want it to be?

In this context, I think so. I suppose "recommended" is a better way
to describe it since we suggest a common set of commands to the user.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 00/16] Add new command 'restore'
  2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
                   ` (11 preceding siblings ...)
  2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
@ 2019-04-11 13:12 ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
                     ` (18 more replies)
  12 siblings, 19 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

This is the companion of "git switch" and is based on that topic.
This command peforms the "checkout paths" from git-checkout, git-reset
and also has a third mode to reset only worktree, leaving the index
alone.

For new people not aware of previous discussions, this command is
supposed to be a friendlier replacement for "git checkout" and
hopefully fixes some warts that have grown over the time in
git-checkout. For this reason, the last patch starts to recommend "git
restore" everywhere.

v2 should address all the comments from v1. I still haven't done that
--intent-to-add thing yet even though I'd like to make it default
behavior too. Anyway changes are

- --index is renamed to --staged to avoid conflict with --index from
  git-apply, which has different meaning.

- lots of document fixes. The comparison between restore/revert/reset
  is added in git.txt instead. I want to avoid duplicate the same
  thing on three man pages.

- --force is dropped. --ignore-unmerged takes its place.

- --recurse-submodules is dropped. I've concluded that restoring files
  across submodule boundary is not exactly supported yet. Let's leave it
  for later.

- git-rm learns about --staged as an alias of --cached (in fact it's
  more the other way around). This is to keep suggestions consistent
  because we tell people to do "git foo --staged" everywhere.

- git-reset and git-diff are moved to other groups in "git help"
  output.

Nguyễn Thái Ngọc Duy (16):
  checkout: split part of it to new command 'restore'
  restore: take tree-ish from --source option instead
  restore: make pathspec mandatory
  restore: disable overlay mode by default
  checkout: factor out worktree checkout code
  restore: add --worktree and --staged
  restore: reject invalid combinations with --staged
  restore: default to --source=HEAD when only --staged is specified
  restore: replace --force with --ignore-unmerged
  restore: support --patch
  t: add tests for restore
  completion: support restore
  user-manual.txt: prefer 'merge --abort' over 'reset --hard'
  doc: promote "git restore"
  rm: add --staged as alias for --cached
  help: move git-diff and git-reset to different groups

 .gitignore                             |   1 +
 Documentation/config/interactive.txt   |   3 +-
 Documentation/git-checkout.txt         |   3 +-
 Documentation/git-clean.txt            |   2 +-
 Documentation/git-commit.txt           |   2 +-
 Documentation/git-format-patch.txt     |   2 +-
 Documentation/git-reset.txt            |  13 +-
 Documentation/git-restore.txt (new)    | 183 +++++++++++++++
 Documentation/git-revert.txt           |   7 +-
 Documentation/git-rm.txt               |   7 +-
 Documentation/git.txt                  |  20 ++
 Documentation/gitcli.txt               |   4 +-
 Documentation/giteveryday.txt          |   5 +-
 Documentation/gittutorial-2.txt        |   4 +-
 Documentation/gittutorial.txt          |   2 +-
 Documentation/user-manual.txt          |  14 +-
 Makefile                               |   1 +
 builtin.h                              |   1 +
 builtin/checkout.c                     | 299 +++++++++++++++++++------
 builtin/clone.c                        |   2 +-
 builtin/commit.c                       |   2 +-
 builtin/rm.c                           |   7 +-
 command-list.txt                       |   7 +-
 contrib/completion/git-completion.bash |  15 ++
 git-add--interactive.perl              |  52 +++++
 git.c                                  |   1 +
 t/lib-patch-mode.sh                    |  12 +
 t/t2070-restore.sh (new +x)            |  99 ++++++++
 t/t2071-restore-patch.sh (new +x)      | 110 +++++++++
 t/t3600-rm.sh                          |   6 +-
 t/t7508-status.sh                      |  84 +++----
 t/t7512-status-help.sh                 |  20 +-
 wt-status.c                            |  30 ++-
 33 files changed, 848 insertions(+), 172 deletions(-)
 create mode 100644 Documentation/git-restore.txt
 create mode 100755 t/t2070-restore.sh
 create mode 100755 t/t2071-restore-patch.sh

Diff:

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ee0d164d0e..a294652dd6 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -570,7 +570,7 @@ $ git add frotz
 
 SEE ALSO
 --------
-linkgit:git-switch[1]
+linkgit:git-switch[1],
 linkgit:git-restore[1]
 
 GIT
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index e7ac8eb9e8..7628193284 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -56,7 +56,7 @@ summary of what is included by any of the above for the next
 commit by giving the same set of parameters (options and paths).
 
 If you make a commit and then find a mistake immediately after
-that, you can recover from it with 'git restore' or 'git reset'.
+that, you can recover from it with 'git reset'.
 
 
 OPTIONS
@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git restore --index <file>`,
+to that of the last commit with `git restore --staged <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 71c9fe3af3..01bfcecf69 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:
 
     $ git fetch <project> master:test-apply
     $ git switch test-apply
-    $ git restore --source=HEAD --index --worktree :/
+    $ git restore --source=HEAD --staged --worktree :/
     $ git am a.patch
 
 If it does not apply correctly, there can be various reasons.
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index b70281677f..633d71d36a 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 	the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
-<paths>`.
+<paths>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
 use linkgit:git-restore[1] to check the contents out of the index to
@@ -86,8 +87,8 @@ but carries forward unmerged index entries.
 	changes, reset is aborted.
 --
 
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
 
 
 OPTIONS
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index a667a5ced4..b608f3f360 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -8,48 +8,60 @@ git-restore - Restore working tree files
 SYNOPSIS
 --------
 [verse]
-'git restore' [<options>] [--source=<revision>] <pathspec>...
-'git restore' (-p|--patch) [--source=<revision>] [<pathspec>...]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
 
 DESCRIPTION
 -----------
-Restore paths in the working tree by replacing with the contents in
-the restore source or remove if the paths do not exist in the restore
-source. The source is by default the index but could be from a commit.
-The command can also optionally restore content in the index from a
-commit.
+Restore specified paths in the working tree with some contents from a
+restore source. If a path is tracked but does not exist in the restore
+source, it will be removed to match the source.
 
-When a `<revision>` is given, the paths that match the `<pathspec>` are
-updated both in the index and in the working tree.
+The command can also be used to restore the content in the index with
+`--staged`, or restore both the working tree and the index with
+`--staged --worktree`.
+
+By default, the restore sources for working tree and the index are the
+index and `HEAD` respectively. `--source` could be used to specify a
+commit as the restore source.
+
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
 
 OPTIONS
 -------
--s<tree>::
+-s <tree>::
 --source=<tree>::
 	Restore the working tree files with the content from the given
-	tree or any revision that leads to a tree (e.g. a commit or a
-	branch).
+	tree. It is common to specify the source tree by naming a
+	commit, branch or tag associated with it.
++
+If not specified, the default restore source for the working tree is
+the index, and the default restore source for the index index is
+`HEAD`. When both `--staged` and `--worktree` are specified,
+`--source` must also be specified.
 
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	`<revision>` (or the index, if unspecified) and the working
-	tree. See the ``Interactive Mode'' section of linkgit:git-add[1]
-	to learn how to operate the `--patch` mode.
+	restore source and the restore location. See the ``Interactive
+	Mode'' section of linkgit:git-add[1] to learn how to operate
+	the `--patch` mode.
++
+Note that `--patch` can accept no pathspec and will prompt to restore
+all modified paths.
 
 -W::
 --worktree::
--I::
---index::
+-S::
+--staged::
 	Specify the restore location. If neither option is specified,
-	by default the working tree is restored. If `--index` is
-	specified without `--worktree` or `--source`, `--source=HEAD`
-	is implied. These options only make sense to use with
-	`--source`.
+	by default the working tree is restored. Specifying `--staged`
+	will only restore the index. Specifying both restores both.
 
 -q::
 --quiet::
-	Quiet, suppress feedback messages.
+	Quiet, suppress feedback messages. Implies `--no-progress`.
 
 --progress::
 --no-progress::
@@ -58,16 +70,10 @@ OPTIONS
 	is specified. This flag enables progress reporting even if not
 	attached to a terminal, regardless of `--quiet`.
 
--f::
---force::
-	If `--source` is not specified, unmerged entries are left alone
-	and will not fail the operation. Unmerged entries are always
-	replaced if `--source` is specified, regardless of `--force`.
-
 --ours::
 --theirs::
-	Check out stage #2 ('ours') or #3 ('theirs') for unmerged
-	paths.
+	When restoring files in the working tree from the index, use
+	stage #2 ('ours') or #3 ('theirs') for unmerged paths.
 +
 Note that during `git rebase` and `git pull --rebase`, 'ours' and
 'theirs' may appear swapped. See the explanation of the same options
@@ -75,67 +81,64 @@ in linkgit:git-checkout[1] for details.
 
 -m::
 --merge::
-	Recreate the conflicted merge in the specified paths.
+	When restoring files on the working tree from the index,
+	recreate the conflicted merge in the unmerged paths.
 
 --conflict=<style>::
 	The same as `--merge` option above, but changes the way the
-	conflicting hunks are presented, overriding the merge.conflictStyle
-	configuration variable.  Possible values are "merge" (default)
-	and "diff3" (in addition to what is shown by "merge" style,
-	shows the original contents).
+	conflicting hunks are presented, overriding the
+	`merge.conflictStyle` configuration variable.  Possible values
+	are "merge" (default) and "diff3" (in addition to what is
+	shown by "merge" style, shows the original contents).
+
+--ignore-unmerged::
+	When restoring files on the working tree from the index, do
+	not abort the operation if there are unmerged entries and
+	neither `--ours`, `--theirs`, `--merge` or `--conflict` is
+	specified. Unmerged paths on the working tree are left alone.
 
 --ignore-skip-worktree-bits::
-	In sparse checkout mode, by default update only entries
+	In sparse checkout mode, by default is to only update entries
 	matched by `<pathspec>` and sparse patterns in
 	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
-	patterns and unconditionally restores any files in `<pathspec>`.
-
---recurse-submodules::
---no-recurse-submodules::
-	Using `--recurse-submodules` will update the content of all initialized
-	submodules according to the commit recorded in the superproject. If
-	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
-	is used, the work trees of submodules will not be updated.
-	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	patterns and unconditionally restores any files in
+	`<pathspec>`.
 
 --overlay::
 --no-overlay::
-	In overlay mode, `git checkout` never removes files from the
-	index or the working tree. In no-overlay mode, files that
-	appear in the index and working tree, but not in `--source` tree
-	are removed, to make them match `<tree-ish>` exactly. The
-	default is no-overlay mode.
+	In overlay mode, the command never removes files when
+	restoring. In no-overlay mode, tracked files that do not
+	appear in the `--source` tree are removed, to make them match
+	`<tree>` exactly. The default is no-overlay mode.
 
 EXAMPLES
 --------
 
-The following sequence checks out the `master` branch, reverts
-the `Makefile` to two revisions back, deletes hello.c by
-mistake, and gets it back from the index.
+The following sequence switches to the `master` branch, reverts the
+`Makefile` to two revisions back, deletes hello.c by mistake, and gets
+it back from the index.
 
 ------------
 $ git switch master
 $ git restore --source master~2 Makefile  <1>
 $ rm -f hello.c
-$ git restore hello.c                   <2>
+$ git restore hello.c                     <2>
 ------------
 
 <1> take a file out of another commit
 <2> restore hello.c from the index
 
-If you want to check out _all_ C source files out of the index,
-you can say
+If you want to restore _all_ C source files to match the version in
+the index, you can say
 
 ------------
 $ git restore '*.c'
 ------------
 
 Note the quotes around `*.c`.  The file `hello.c` will also be
-checked out, even though it is no longer in the working tree,
-because the file globbing is used to match entries in the index
-(not in the working tree by the shell).
+restored, even though it is no longer in the working tree, because the
+file globbing is used to match entries in the index (not in the
+working tree by the shell).
 
 To restore all files in the current directory
 
@@ -144,28 +147,36 @@ $ git restore .
 ------------
 
 or to restore all working tree files with 'top' pathspec magic (see
-linkgit::gitglossary[7])
+linkgit:gitglossary[7])
 
 ------------
 $ git restore :/
 ------------
 
-To restore a file in the index only (this is the same as using
-"git reset")
+To restore a file in the index to match the version in `HEAD` (this is
+the same as using linkgit:git-reset[1])
+
+------------
+$ git restore --staged hello.c
+------------
+
+or you can restore both the index and the working tree (this the same
+as using linkgit:git-checkout[1])
 
 ------------
-$ git restore --index hello.c
+$ git restore --source=HEAD --staged --worktree hello.c
 ------------
 
-or you can restore both the index and the working tree
+or the short form which is more practical but less readable:
 
 ------------
-$ git restore --source=HEAD --index --worktree hello.c
+$ git restore -s@ -SW hello.c
 ------------
 
 SEE ALSO
 --------
-linkgit:git-checkout[1]
+linkgit:git-checkout[1],
+linkgit:git-reset[1]
 
 GIT
 ---
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index c38bc54439..9aadc36881 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -27,9 +27,12 @@ throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the `--hard` option.  If
 you want to extract specific files as they were in another commit, you
 should see linkgit:git-restore[1], specifically the `--source`
-option  Take care with these alternatives as
+option. Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
 OPTIONS
 -------
 <commit>...::
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b5c46223c4..4271fc5eaa 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,7 +8,7 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
+'git rm' [-f | --force] [-n] [-r] [--staged] [--ignore-unmatch] [--quiet] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -55,10 +55,11 @@ OPTIONS
 	the list of files, (useful when filenames might be mistaken
 	for command-line options).
 
+--staged::
 --cached::
 	Use this option to unstage and remove paths only from the index.
-	Working tree files, whether modified or not, will be
-	left alone.
+	Working tree files, whether modified or not, will be left
+	alone. `--cached` is synonym for `--staged`.
 
 --ignore-unmatch::
 	Exit with a zero status even if no files matched.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 00156d64aa..fbed007354 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -210,6 +210,26 @@ people via patch over e-mail.
 
 include::cmds-foreignscminterface.txt[]
 
+Reset, restore and revert
+~~~~~~~~~~~~~~~~~~~~~~~~~
+There are three commands with similar names: `git reset`,
+`git restore` and `git revert`.
+
+* linkgit:git-revert[1] is about making a new commit that reverts the
+  changes made by other commits.
+
+* linkgit:git-restore[1] is about restoring files in the working tree
+  from either the index or another commit. This command does not
+  update your branch. The command can also be used to restore files in
+  the index from another commit.
+
+* linkgit:git-reset[1] is about updating your branch, moving the tip
+  in order to add or remove commits from the branch. This operation
+  changes the commit history.
++
+`git reset` can also be used to restore the index, overlapping with
+`git restore`.
+
 
 Low-level commands (plumbing)
 -----------------------------
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 79517b22f9..1bd919f92b 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -51,8 +51,7 @@ following commands.
 
   * linkgit:git-commit[1] to advance the current branch.
 
-  * linkgit:git-reset[1] and linkgit:git-restore[1] (with
-    pathname parameters) to undo changes.
+  * linkgit:git-restore[1] to undo changes.
 
   * linkgit:git-merge[1] to merge between local branches.
 
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index e412841488..8bdb7d0bd3 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -370,7 +370,7 @@ situation:
 $ git status
 On branch master
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   closing.txt
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 6266ca21b4..59ef5cef1f 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -110,7 +110,7 @@ $ git status
 On branch master
 Changes to be committed:
 Your branch is up to date with 'origin/master'.
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   file1
 	modified:   file2
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 02713886f0..8bce75b2cf 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed
 state with
 
 -------------------------------------------------
-$ git restore --index --worktree :/
+$ git restore --staged --worktree :/
 -------------------------------------------------
 
 If you make a commit that you later wish you hadn't, there are two
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e8896ddbca..bed79ae595 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -38,7 +38,7 @@ static const char * const switch_branch_usage[] = {
 	NULL,
 };
 
-static const char * const restore_files_usage[] = {
+static const char * const restore_usage[] = {
 	N_("git restore [<options>] [--source=<branch>] <file>..."),
 	NULL,
 };
@@ -68,6 +68,8 @@ struct checkout_opts {
 	int empty_pathspec_ok;
 	int checkout_index;
 	int checkout_worktree;
+	const char *ignore_unmerged_opt;
+	int ignore_unmerged;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->new_branch_log)
 		die(_("'%s' cannot be used with updating paths"), "-l");
 
-	if (opts->force && opts->patch_mode)
-		die(_("'%s' cannot be used with updating paths"), "-f");
+	if (opts->ignore_unmerged && opts->patch_mode)
+		die(_("'%s' cannot be used with updating paths"),
+		    opts->ignore_unmerged_opt);
 
 	if (opts->force_detach)
 		die(_("'%s' cannot be used with updating paths"), "--detach");
@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->merge && opts->patch_mode)
 		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
 
-	if (opts->force && opts->merge)
-		die(_("'%s' cannot be used with %s"), "-f", "-m");
+	if (opts->ignore_unmerged && opts->merge)
+		die(_("'%s' cannot be used with %s"),
+		    opts->ignore_unmerged_opt, "-m");
 
 	if (opts->new_branch)
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
@@ -427,12 +431,22 @@ static int checkout_paths(const struct checkout_opts *opts,
 
 	if (!opts->checkout_worktree && !opts->checkout_index)
 		die(_("neither '%s' or '%s' is specified"),
-		    "--index", "--worktree");
+		    "--staged", "--worktree");
 
-	if (!opts->source_tree && !opts->checkout_worktree)
+	if (!opts->checkout_worktree && !opts->from_treeish)
 		die(_("'%s' must be used when '%s' is not specified"),
 		    "--worktree", "--source");
 
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->writeout_stage)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--ours", "--theirs", "--staged");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->merge)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--merge", "--conflict", "--staged");
+
 	if (opts->patch_mode) {
 		const char *patch_mode;
 
@@ -443,7 +457,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		else if (!opts->checkout_index && opts->checkout_worktree)
 			patch_mode = "--patch=worktree";
 		else
-			BUG("either flag must have been set");
+			BUG("either flag must have been set, worktree=%d, index=%d",
+			    opts->checkout_worktree, opts->checkout_index);
 		return run_add_interactive(revision, patch_mode, &opts->pathspec);
 	}
 
@@ -486,8 +501,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
-			if (opts->force) {
-				warning(_("path '%s' is unmerged"), ce->name);
+			if (opts->ignore_unmerged) {
+				if (!opts->quiet)
+					warning(_("path '%s' is unmerged"), ce->name);
 			} else if (opts->writeout_stage) {
 				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
 			} else if (opts->merge) {
@@ -1405,8 +1421,6 @@ static struct option *add_common_options(struct checkout_opts *opts,
 			    "checkout", "control recursive updating of submodules",
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
 		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
 		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
 		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
@@ -1424,6 +1438,8 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
@@ -1493,8 +1509,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
-	if (opts->force)
+	if (opts->force) {
 		opts->discard_changes = 1;
+		opts->ignore_unmerged_opt = "--force";
+		opts->ignore_unmerged = 1;
+	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1516,8 +1535,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
 		BUG("these flags should be non-negative by now");
 	/*
-	 * convenient shortcut: "git restore --index" equals
-	 * "git restore --index --source HEAD"
+	 * convenient shortcut: "git restore --staged" equals
+	 * "git restore --staged --source HEAD"
 	 */
 	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
 		opts->from_treeish = "HEAD";
@@ -1587,7 +1606,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 
 	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
 	    !opts->patch_mode)	/* patch mode is special */
-		die(_("pathspec is required"));
+		die(_("you must specify path(s) to restore"));
 
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
@@ -1737,10 +1756,12 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
-		OPT_BOOL('I', "index", &opts.checkout_index,
+		OPT_BOOL('S', "staged", &opts.checkout_index,
 			   N_("restore the index")),
 		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
 			   N_("restore the working tree (default)")),
+		OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+			 N_("ignore unmerged entries")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1753,13 +1774,14 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.overlay_mode = 0;
 	opts.checkout_index = -1;    /* default off */
 	opts.checkout_worktree = -2; /* default on */
+	opts.ignore_unmerged_opt = "--ignore-unmerged";
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
-			    options, restore_files_usage);
+			    options, restore_usage);
 	FREE_AND_NULL(options);
 	return ret;
 }
diff --git a/builtin/commit.c b/builtin/commit.c
index c530264e63..fa5982cc86 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
-		      "not exceeded, and then \"git restore --index :/\" to recover."));
+		      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
 	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 		write_commit_graph_reachable(get_object_directory(), 0, 0);
diff --git a/builtin/rm.c b/builtin/rm.c
index db85b33982..47c8eb100b 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -217,7 +217,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 			     "staged in the index:",
 			     "the following files have changes "
 			     "staged in the index:", files_cached.nr),
-			  _("\n(use --cached to keep the file,"
+			  _("\n(use --staged to keep the file,"
 			    " or -f to force removal)"),
 			  &errs);
 	string_list_clear(&files_cached, 0);
@@ -226,7 +226,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 			  Q_("the following file has local modifications:",
 			     "the following files have local modifications:",
 			     files_local.nr),
-			  _("\n(use --cached to keep the file,"
+			  _("\n(use --staged to keep the file,"
 			    " or -f to force removal)"),
 			  &errs);
 	string_list_clear(&files_local, 0);
@@ -240,7 +240,8 @@ static int ignore_unmatch = 0;
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only, N_("dry run")),
 	OPT__QUIET(&quiet, N_("do not list removed files")),
-	OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),
+	OPT_BOOL( 0 , "staged",         &index_only, N_("only remove from the index")),
+	OPT_BOOL( 0 , "cached",         &index_only, N_("synonym for --staged")),
 	OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE),
 	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
 	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
diff --git a/command-list.txt b/command-list.txt
index cf8dccb439..a9ac72bef4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
 git-daemon                              synchingrepositories
 git-describe                            mainporcelain
-git-diff                                mainporcelain           history
+git-diff                                mainporcelain           info
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
-git-reset                               mainporcelain           worktree
+git-reset                               mainporcelain           history
 git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index df91bf54bc..73ea13ede9 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -23,11 +23,6 @@ test_expect_success 'restore without pathspec is not ok' '
 	test_must_fail git restore --source=first
 '
 
-test_expect_success 'restore -p without pathspec is fine' '
-	echo q >cmd &&
-	git restore -p <cmd
-'
-
 test_expect_success 'restore a file, ignoring branch of same name' '
 	cat one >expected &&
 	echo dirty >>one &&
@@ -35,7 +30,7 @@ test_expect_success 'restore a file, ignoring branch of same name' '
 	test_cmp expected one
 '
 
-test_expect_success 'restore a file on worktree from another branch' '
+test_expect_success 'restore a file on worktree from another ref' '
 	test_when_finished git reset --hard &&
 	git cat-file blob first:./first.t >expected &&
 	git restore --source=first first.t &&
@@ -45,33 +40,60 @@ test_expect_success 'restore a file on worktree from another branch' '
 	test_cmp expected actual
 '
 
-test_expect_success 'restore a file in the index from another branch' '
+test_expect_success 'restore a file in the index from another ref' '
 	test_when_finished git reset --hard &&
 	git cat-file blob first:./first.t >expected &&
-	git restore --source=first --index first.t &&
+	git restore --source=first --staged first.t &&
 	git show :first.t >actual &&
 	test_cmp expected actual &&
 	git cat-file blob HEAD:./first.t >expected &&
 	test_cmp expected first.t
 '
 
-test_expect_success 'restore a file in both the index and worktree from another branch' '
+test_expect_success 'restore a file in both the index and worktree from another ref' '
 	test_when_finished git reset --hard &&
 	git cat-file blob first:./first.t >expected &&
-	git restore --source=first --index --worktree first.t &&
+	git restore --source=first --staged --worktree first.t &&
 	git show :first.t >actual &&
 	test_cmp expected actual &&
 	test_cmp expected first.t
 '
 
-test_expect_success 'restore --index uses HEAD as source' '
+test_expect_success 'restore --staged uses HEAD as source' '
 	test_when_finished git reset --hard &&
 	git cat-file blob :./first.t >expected &&
 	echo index-dirty >>first.t &&
 	git add first.t &&
-	git restore --index first.t &&
+	git restore --staged first.t &&
 	git cat-file blob :./first.t >actual &&
 	test_cmp expected actual
 '
 
+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
+	git init unmerged &&
+	(
+		cd unmerged &&
+		echo one >unmerged &&
+		echo one >common &&
+		git add unmerged common &&
+		git commit -m common &&
+		git switch -c first &&
+		echo first >unmerged &&
+		git commit -am first &&
+		git switch -c second master &&
+		echo second >unmerged &&
+		git commit -am second &&
+		test_must_fail git merge first &&
+
+		echo dirty >>common &&
+		test_must_fail git restore . &&
+
+		git restore --ignore-unmerged --quiet . >output 2>&1 &&
+		git diff common >diff-output &&
+		: >empty &&
+		test_cmp empty output &&
+		test_cmp empty diff-output
+	)
+'
+
 test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index 46ebcb2413..98b2476e7c 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -16,6 +16,11 @@ test_expect_success PERL 'setup' '
 	save_head
 '
 
+test_expect_success PERL 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
 # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
 
 test_expect_success PERL 'saying "n" does nothing' '
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 04e5d42bd3..5686032b8c 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -797,7 +797,7 @@ test_expect_success 'rm file with local modification' '
 	cat >expect <<-\EOF &&
 	error: the following file has local modifications:
 	    foo.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	git commit -m "testing rm 3" &&
 	echo content3 >foo.txt &&
@@ -819,7 +819,7 @@ test_expect_success 'rm file with changes in the index' '
 	cat >expect <<-\EOF &&
 	error: the following file has changes staged in the index:
 	    foo.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	git reset --hard &&
 	echo content5 >foo.txt &&
@@ -845,7 +845,7 @@ test_expect_success 'rm files with two different errors' '
 	(use -f to force removal)
 	error: the following file has changes staged in the index:
 	    bar1.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	echo content >foo1.txt &&
 	git add foo1.txt &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index d5cf0185b7..738f3df2f9 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -71,7 +71,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'status (1)' '
-	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+	test_i18ngrep "use \"git rm --staged <file>\.\.\.\" to unstage" output
 '
 
 strip_comments () {
@@ -94,7 +94,7 @@ test_expect_success 'status --column' '
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git restore --index <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
@@ -128,7 +128,7 @@ cat >expect <<\EOF
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git restore --index <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
@@ -278,7 +278,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -347,7 +347,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -420,7 +420,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -484,7 +484,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -542,7 +542,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -605,7 +605,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   ../dir2/added
 
@@ -676,7 +676,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	<GREEN>new file:   dir2/added<RESET>
 
@@ -802,7 +802,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   dir1/modified
 
@@ -896,7 +896,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
@@ -956,7 +956,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
@@ -1068,7 +1068,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --source=HEAD^1 --index <file>..." to unstage)
+  (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
@@ -1123,7 +1123,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
@@ -1235,7 +1235,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
@@ -1295,7 +1295,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
@@ -1379,7 +1379,7 @@ cat > expect << EOF
 ;   (use "git pull" to merge the remote branch into yours)
 ;
 ; Changes to be committed:
-;   (use "git restore --index <file>..." to unstage)
+;   (use "git restore --staged <file>..." to unstage)
 ;
 ;	modified:   sm
 ;
@@ -1458,7 +1458,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
@@ -1581,7 +1581,7 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 6e678456bc..1b9712c675 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -85,7 +85,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -110,7 +110,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -148,7 +148,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -176,7 +176,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Unmerged paths:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   to-revert.txt
@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Changes to be committed:
-  (use "git restore --index <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   to-revert.txt
 
diff --git a/wt-status.c b/wt-status.c
index 3098e77d72..4d065ce89e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -181,13 +181,13 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
 	else if (!s->is_initial) {
 		if (!strcmp(s->reference, "HEAD"))
 			status_printf_ln(s, c,
-					 _("  (use \"git restore --index <file>...\" to unstage)"));
+					 _("  (use \"git restore --staged <file>...\" to unstage)"));
 		else
 			status_printf_ln(s, c,
-					 _("  (use \"git restore --source=%s --index <file>...\" to unstage)"),
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 					 s->reference);
 	} else
-		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+		status_printf_ln(s, c, _("  (use \"git rm --staged <file>...\" to unstage)"));
 
 	if (!both_deleted) {
 		if (!del_mod_conflict)
@@ -214,13 +214,13 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
 	else if (!s->is_initial) {
 		if (!strcmp(s->reference, "HEAD"))
 			status_printf_ln(s, c
-					 , _("  (use \"git restore --index <file>...\" to unstage)"));
+					 , _("  (use \"git restore --staged <file>...\" to unstage)"));
 		else
 			status_printf_ln(s, c,
-					 _("  (use \"git restore --source=%s --index <file>...\" to unstage)"),
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 					 s->reference);
 	} else
-		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+		status_printf_ln(s, c, _("  (use \"git rm --staged <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
 


-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 01/16] checkout: split part of it to new command 'restore'
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` " Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
                     ` (17 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Previously the switching branch business of 'git checkout' becomes a
new command 'switch'. This adds the restore command for the checking
out paths path.

Similar to git-switch, a new man page is added to describe what the
command will become. The implementation will be updated shortly to
match the man page.

A couple main differences from 'git checkout <paths>':

- 'restore' by default will only update worktree. This matters more
  when --source is specified ('checkout <tree> <paths>' updates both
  worktree and index).

- 'restore --staged' can be used to restore the index. This command
  overlaps with 'git reset <paths>'.

- both worktree and index could also be restored at the same time
  (from a tree) when both --staged and --worktree are specified. This
  overlaps with 'git checkout <tree> <paths>'

- default source for restoring worktree and index is the index and
  HEAD respectively. A different (tree) source could be specified as
  with --source (*).

- when both index and worktree are restored, --source must be
  specified since the default source for these two individual targets
  are different (**)

- --no-overlay is enabled by default, if an entry is missing in the
  source, restoring means deleting the entry

(*) I originally went with --from instead of --source. I still think
  --from is a better name. The short option -f however is already
  taken by force. And I do think short option is good to have, e.g. to
  write -s@ or -s@^ instead of --source=HEAD.

(**) If you sit down and think about it, moving worktree's source from
  the index to HEAD makes sense, but nobody is really thinking it
  through when they type the commands.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                           |   1 +
 Documentation/config/interactive.txt |   3 +-
 Documentation/git-checkout.txt       |   3 +-
 Documentation/git-reset.txt          |   7 +-
 Documentation/git-restore.txt (new)  | 183 +++++++++++++++++++++++++++
 Documentation/git-revert.txt         |   3 +
 Documentation/git.txt                |  20 +++
 Makefile                             |   1 +
 builtin.h                            |   1 +
 builtin/checkout.c                   |  26 ++++
 command-list.txt                     |   1 +
 git.c                                |   1 +
 12 files changed, 245 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index c687b92b1c..fb377106be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@
 /git-request-pull
 /git-rerere
 /git-reset
+/git-restore
 /git-rev-list
 /git-rev-parse
 /git-revert
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index ad846dd7c9..a2d3c7ec44 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -2,7 +2,8 @@ interactive.singleKey::
 	In interactive commands, allow the user to provide one-letter
 	input with a single key (i.e., without hitting enter).
 	Currently this is used by the `--patch` mode of
-	linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+	linkgit:git-add[1], linkgit:git-checkout[1],
+	linkgit:git-restore[1], linkgit:git-commit[1],
 	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
 	setting is silently ignored if portable keystroke input
 	is not available; requires the Perl module Term::ReadKey.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 58f18a0842..a294652dd6 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -570,7 +570,8 @@ $ git add frotz
 
 SEE ALSO
 --------
-linkgit:git-switch[1]
+linkgit:git-switch[1],
+linkgit:git-restore[1]
 
 GIT
 ---
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index cbf901efb4..c25f8a95b9 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 	the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
-<paths>`.
+<paths>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
 use linkgit:git-checkout[1] to check the contents out of the index to
@@ -86,8 +87,8 @@ but carries forward unmerged index entries.
 	changes, reset is aborted.
 --
 
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
 
 
 OPTIONS
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
new file mode 100644
index 0000000000..b608f3f360
--- /dev/null
+++ b/Documentation/git-restore.txt
@@ -0,0 +1,183 @@
+git-restore(1)
+==============
+
+NAME
+----
+git-restore - Restore working tree files
+
+SYNOPSIS
+--------
+[verse]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+
+DESCRIPTION
+-----------
+Restore specified paths in the working tree with some contents from a
+restore source. If a path is tracked but does not exist in the restore
+source, it will be removed to match the source.
+
+The command can also be used to restore the content in the index with
+`--staged`, or restore both the working tree and the index with
+`--staged --worktree`.
+
+By default, the restore sources for working tree and the index are the
+index and `HEAD` respectively. `--source` could be used to specify a
+commit as the restore source.
+
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
+OPTIONS
+-------
+-s <tree>::
+--source=<tree>::
+	Restore the working tree files with the content from the given
+	tree. It is common to specify the source tree by naming a
+	commit, branch or tag associated with it.
++
+If not specified, the default restore source for the working tree is
+the index, and the default restore source for the index index is
+`HEAD`. When both `--staged` and `--worktree` are specified,
+`--source` must also be specified.
+
+-p::
+--patch::
+	Interactively select hunks in the difference between the
+	restore source and the restore location. See the ``Interactive
+	Mode'' section of linkgit:git-add[1] to learn how to operate
+	the `--patch` mode.
++
+Note that `--patch` can accept no pathspec and will prompt to restore
+all modified paths.
+
+-W::
+--worktree::
+-S::
+--staged::
+	Specify the restore location. If neither option is specified,
+	by default the working tree is restored. Specifying `--staged`
+	will only restore the index. Specifying both restores both.
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages. Implies `--no-progress`.
+
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless `--quiet`
+	is specified. This flag enables progress reporting even if not
+	attached to a terminal, regardless of `--quiet`.
+
+--ours::
+--theirs::
+	When restoring files in the working tree from the index, use
+	stage #2 ('ours') or #3 ('theirs') for unmerged paths.
++
+Note that during `git rebase` and `git pull --rebase`, 'ours' and
+'theirs' may appear swapped. See the explanation of the same options
+in linkgit:git-checkout[1] for details.
+
+-m::
+--merge::
+	When restoring files on the working tree from the index,
+	recreate the conflicted merge in the unmerged paths.
+
+--conflict=<style>::
+	The same as `--merge` option above, but changes the way the
+	conflicting hunks are presented, overriding the
+	`merge.conflictStyle` configuration variable.  Possible values
+	are "merge" (default) and "diff3" (in addition to what is
+	shown by "merge" style, shows the original contents).
+
+--ignore-unmerged::
+	When restoring files on the working tree from the index, do
+	not abort the operation if there are unmerged entries and
+	neither `--ours`, `--theirs`, `--merge` or `--conflict` is
+	specified. Unmerged paths on the working tree are left alone.
+
+--ignore-skip-worktree-bits::
+	In sparse checkout mode, by default is to only update entries
+	matched by `<pathspec>` and sparse patterns in
+	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
+	patterns and unconditionally restores any files in
+	`<pathspec>`.
+
+--overlay::
+--no-overlay::
+	In overlay mode, the command never removes files when
+	restoring. In no-overlay mode, tracked files that do not
+	appear in the `--source` tree are removed, to make them match
+	`<tree>` exactly. The default is no-overlay mode.
+
+EXAMPLES
+--------
+
+The following sequence switches to the `master` branch, reverts the
+`Makefile` to two revisions back, deletes hello.c by mistake, and gets
+it back from the index.
+
+------------
+$ git switch master
+$ git restore --source master~2 Makefile  <1>
+$ rm -f hello.c
+$ git restore hello.c                     <2>
+------------
+
+<1> take a file out of another commit
+<2> restore hello.c from the index
+
+If you want to restore _all_ C source files to match the version in
+the index, you can say
+
+------------
+$ git restore '*.c'
+------------
+
+Note the quotes around `*.c`.  The file `hello.c` will also be
+restored, even though it is no longer in the working tree, because the
+file globbing is used to match entries in the index (not in the
+working tree by the shell).
+
+To restore all files in the current directory
+
+------------
+$ git restore .
+------------
+
+or to restore all working tree files with 'top' pathspec magic (see
+linkgit:gitglossary[7])
+
+------------
+$ git restore :/
+------------
+
+To restore a file in the index to match the version in `HEAD` (this is
+the same as using linkgit:git-reset[1])
+
+------------
+$ git restore --staged hello.c
+------------
+
+or you can restore both the index and the working tree (this the same
+as using linkgit:git-checkout[1])
+
+------------
+$ git restore --source=HEAD --staged --worktree hello.c
+------------
+
+or the short form which is more practical but less readable:
+
+------------
+$ git restore -s@ -SW hello.c
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-reset[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 837707a8fd..018ecf49d3 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -30,6 +30,9 @@ should see linkgit:git-checkout[1], specifically the `git checkout
 <commit> -- <filename>` syntax.  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
 OPTIONS
 -------
 <commit>...::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 00156d64aa..fbed007354 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -210,6 +210,26 @@ people via patch over e-mail.
 
 include::cmds-foreignscminterface.txt[]
 
+Reset, restore and revert
+~~~~~~~~~~~~~~~~~~~~~~~~~
+There are three commands with similar names: `git reset`,
+`git restore` and `git revert`.
+
+* linkgit:git-revert[1] is about making a new commit that reverts the
+  changes made by other commits.
+
+* linkgit:git-restore[1] is about restoring files in the working tree
+  from either the index or another commit. This command does not
+  update your branch. The command can also be used to restore files in
+  the index from another commit.
+
+* linkgit:git-reset[1] is about updating your branch, moving the tip
+  in order to add or remove commits from the branch. This operation
+  changes the commit history.
++
+`git reset` can also be used to restore the index, overlapping with
+`git restore`.
+
 
 Low-level commands (plumbing)
 -----------------------------
diff --git a/Makefile b/Makefile
index 8e91db73ad..ffe7e4f58f 100644
--- a/Makefile
+++ b/Makefile
@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
+BUILT_INS += git-restore$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
diff --git a/builtin.h b/builtin.h
index c64e44450e..6830000e14 100644
--- a/builtin.h
+++ b/builtin.h
@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_repack(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
+extern int cmd_restore(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0351735c6e..98dc2ded38 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -38,6 +38,11 @@ static const char * const switch_branch_usage[] = {
 	NULL,
 };
 
+static const char * const restore_usage[] = {
+	N_("git restore [<options>] [<branch>] -- <file>..."),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1622,3 +1627,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	FREE_AND_NULL(options);
 	return ret;
 }
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_pathspec = 1;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, restore_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 13317f47d4..b9eae1c258 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           worktree
+git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
 git-rev-parse                           plumbinginterrogators
diff --git a/git.c b/git.c
index 39582cf511..6d439e723f 100644
--- a/git.c
+++ b/git.c
@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "replace", cmd_replace, RUN_SETUP },
 	{ "rerere", cmd_rerere, RUN_SETUP },
 	{ "reset", cmd_reset, RUN_SETUP },
+	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
 	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
 	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
 	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 02/16] restore: take tree-ish from --source option instead
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
                     ` (16 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

This is another departure from 'git checkout' syntax, which uses -- to
separate ref and pathspec. The observation is restore (or "git
checkout -- <pathspec>") is most often used to restore some files from
the index. If this is correct, we can simplify it by taking away the
ref, so that we can write

    git restore some-file

without worrying about some-file being a ref and whether we need to do

    git restore -- some-file

for safety. If the source of the restore comes from a tree, it will be
in the form of an option with value, e.g.

    git restore --source=this-tree some-file

This is of course longer to type than using "--". But hopefully it
will not be used as often, and it is clearly easier to understand.

dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()
because it's irrelevant because we don't really care about dwim-ing.
With accept_ref being unset, dwim can't happen.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 98dc2ded38..5aba345712 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -39,7 +39,7 @@ static const char * const switch_branch_usage[] = {
 };
 
 static const char * const restore_usage[] = {
-	N_("git restore [<options>] [<branch>] -- <file>..."),
+	N_("git restore [<options>] [--source=<branch>] <file>..."),
 	NULL,
 };
 
@@ -59,6 +59,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int dwim_new_local_branch;
 	int discard_changes;
+	int accept_ref;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
@@ -76,6 +77,7 @@ struct checkout_opts {
 	int branch_exists;
 	const char *prefix;
 	struct pathspec pathspec;
+	const char *from_treeish;
 	struct tree *source_tree;
 };
 
@@ -1410,6 +1412,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
+	int parseopt_flags = 0;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1421,8 +1424,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	argc = parse_options(argc, argv, prefix, options, usagestr,
-			     PARSE_OPT_KEEP_DASHDASH);
+	if (!opts->accept_pathspec && !opts->accept_ref)
+		BUG("make up your mind, you need to take _something_");
+	if (opts->accept_pathspec && opts->accept_ref)
+		parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     usagestr, parseopt_flags);
 
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
@@ -1481,7 +1489,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	 * including "last branch" syntax and DWIM-ery for names of
 	 * remote branches, erroring out for invalid or ambiguous cases.
 	 */
-	if (argc) {
+	if (argc && opts->accept_ref) {
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
@@ -1493,6 +1501,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
+	} else if (!opts->accept_ref && opts->from_treeish) {
+		struct object_id rev;
+
+		if (get_oid_mb(opts->from_treeish, &rev))
+			die(_("could not resolve %s"), opts->from_treeish);
+
+		setup_new_branch_info_and_source_tree(&new_branch_info,
+						      opts, &rev,
+						      opts->from_treeish);
+
+		if (!opts->source_tree)
+			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
 	if (argc) {
@@ -1576,6 +1596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.only_merge_on_switching_branches = 0;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
@@ -1611,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
@@ -1631,15 +1653,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 int cmd_restore(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option restore_options[] = {
+		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+			   N_("where the checkout from")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.dwim_new_local_branch = 1;
-	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 03/16] restore: make pathspec mandatory
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
                     ` (15 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

"git restore" without arguments does not make much sense when
it's about restoring files (what files now?). We could default to
either

    git restore .

or

    git restore :/

Neither is intuitive. Make the user always give pathspec, force the
user to think the scope of restore they want because this is a
destructive operation.

"git restore -p" without pathspec is an exception to this
because it really is a separate mode. It will be treated as running
patch mode on the whole worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5aba345712..77db5236f0 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -65,6 +65,7 @@ struct checkout_opts {
 	int only_merge_on_switching_branches;
 	int can_switch_when_in_progress;
 	int orphan_from_empty_tree;
+	int empty_pathspec_ok;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1515,6 +1516,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
+	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+	    !opts->patch_mode)	/* patch mode is special */
+		die(_("you must specify path(s) to restore"));
+
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
 			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1601,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
 	opts.orphan_from_empty_tree = 0;
+	opts.empty_pathspec_ok = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1664,6 +1670,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
+	opts.empty_pathspec_ok = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 04/16] restore: disable overlay mode by default
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (2 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
                     ` (14 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Overlay mode is considered confusing when the command is about
restoring files on worktree. Disable it by default. The user can still
turn it on, or use 'git checkout' which still has overlay mode on by
default.

While at it, make the check in checkout_branch() stricter. Neither
--overlay or --no-overlay should be accepted in branch switching mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 77db5236f0..9c5abe170e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1274,9 +1274,9 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
 
-	if (!opts->overlay_mode)
+	if (opts->overlay_mode != -1)
 		die(_("'%s' cannot be used with switching branches"),
-		    "--no-overlay");
+		    "--[no]-overlay");
 
 	if (opts->writeout_stage)
 		die(_("'%s' cannot be used with switching branches"),
@@ -1399,7 +1399,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	struct option *newopts = parse_options_concat(prevopts, options);
@@ -1419,7 +1418,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
-	opts->overlay_mode = -1;
 
 	git_config(git_checkout_config, opts);
 
@@ -1593,6 +1591,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
 			 N_("second guess 'git checkout <no-such-branch>' (default)")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	int ret;
@@ -1607,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.can_switch_when_in_progress = 1;
 	opts.orphan_from_empty_tree = 0;
 	opts.empty_pathspec_ok = 1;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1645,6 +1645,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.implicit_detach = 0;
 	opts.can_switch_when_in_progress = 0;
 	opts.orphan_from_empty_tree = 1;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
@@ -1663,6 +1664,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
 	int ret;
@@ -1671,6 +1673,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
+	opts.overlay_mode = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 05/16] checkout: factor out worktree checkout code
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (3 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
                     ` (13 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------
 1 file changed, 59 insertions(+), 49 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9c5abe170e..1753c8c5ed 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -330,17 +330,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 	}
 }
 
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+	struct checkout state = CHECKOUT_INIT;
+	int nr_checkouts = 0, nr_unmerged = 0;
+	int errs = 0;
+	int pos;
+
+	state.force = 1;
+	state.refresh_cache = 1;
+	state.istate = &the_index;
+
+	enable_delayed_checkout(&state);
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce->ce_flags & CE_MATCHED) {
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state,
+						       NULL, &nr_checkouts);
+				continue;
+			}
+			if (opts->writeout_stage)
+				errs |= checkout_stage(opts->writeout_stage,
+						       ce, pos,
+						       &state,
+						       &nr_checkouts, opts->overlay_mode);
+			else if (opts->merge)
+				errs |= checkout_merged(pos, &state,
+							&nr_unmerged);
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	remove_marked_cache_entries(&the_index, 1);
+	remove_scheduled_dirs();
+	errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+	if (opts->count_checkout_paths) {
+		if (nr_unmerged)
+			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+					      "Recreated %d merge conflicts",
+					      nr_unmerged),
+				   nr_unmerged);
+		if (opts->source_tree)
+			fprintf_ln(stderr, Q_("Updated %d path from %s",
+					      "Updated %d paths from %s",
+					      nr_checkouts),
+				   nr_checkouts,
+				   find_unique_abbrev(&opts->source_tree->object.oid,
+						      DEFAULT_ABBREV));
+		else if (!nr_unmerged || nr_checkouts)
+			fprintf_ln(stderr, Q_("Updated %d path from the index",
+					      "Updated %d paths from the index",
+					      nr_checkouts),
+				   nr_checkouts);
+	}
+
+	return errs;
+}
+
 static int checkout_paths(const struct checkout_opts *opts,
 			  const char *revision)
 {
 	int pos;
-	struct checkout state = CHECKOUT_INIT;
 	static char *ps_matched;
 	struct object_id rev;
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
-	int nr_checkouts = 0, nr_unmerged = 0;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -426,53 +482,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	state.force = 1;
-	state.refresh_cache = 1;
-	state.istate = &the_index;
-
-	enable_delayed_checkout(&state);
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		if (ce->ce_flags & CE_MATCHED) {
-			if (!ce_stage(ce)) {
-				errs |= checkout_entry(ce, &state,
-						       NULL, &nr_checkouts);
-				continue;
-			}
-			if (opts->writeout_stage)
-				errs |= checkout_stage(opts->writeout_stage,
-						       ce, pos,
-						       &state,
-						       &nr_checkouts, opts->overlay_mode);
-			else if (opts->merge)
-				errs |= checkout_merged(pos, &state,
-							&nr_unmerged);
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-	remove_marked_cache_entries(&the_index, 1);
-	remove_scheduled_dirs();
-	errs |= finish_delayed_checkout(&state, &nr_checkouts);
-
-	if (opts->count_checkout_paths) {
-		if (nr_unmerged)
-			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
-					      "Recreated %d merge conflicts",
-					      nr_unmerged),
-				   nr_unmerged);
-		if (opts->source_tree)
-			fprintf_ln(stderr, Q_("Updated %d path from %s",
-					      "Updated %d paths from %s",
-					      nr_checkouts),
-				   nr_checkouts,
-				   find_unique_abbrev(&opts->source_tree->object.oid,
-						      DEFAULT_ABBREV));
-		else if (!nr_unmerged || nr_checkouts)
-			fprintf_ln(stderr, Q_("Updated %d path from the index",
-					      "Updated %d paths from the index",
-					      nr_checkouts),
-				   nr_checkouts);
-	}
+	errs |= checkout_worktree(opts);
 
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 06/16] restore: add --worktree and --staged
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (4 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
                     ` (12 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

'git checkout <tree-ish> <pathspec>' updates both index and
worktree. But updating the index when you want to restore worktree
files is non-intuitive. The index contains the data ready for the next
commit, and there's no indication that the user will want to commit
the restored versions.

'git restore' therefore by default only touches worktree. The user has
the option to update either the index with

    git restore --staged --source=<tree> <path>  (1)

or update both with

    git restore --staged --worktree --source=<tree> <path> (2)

PS. Orignally I wanted to make worktree update default and form (1)
would add index update while also updating the worktree, and the user
would need to do "--staged --no-worktree" to update index only. But it
looks really confusing that "--staged" option alone updates both. So
now form (2) is used for both, which reads much more obvious.

PPS. Yes form (1) overlaps with "git reset <rev> <path>". I don't know
if we can ever turn "git reset" back to "_always_ reset HEAD and
optionally do something else".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 6 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1753c8c5ed..e855c64cfe 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -66,6 +66,8 @@ struct checkout_opts {
 	int can_switch_when_in_progress;
 	int orphan_from_empty_tree;
 	int empty_pathspec_ok;
+	int checkout_index;
+	int checkout_worktree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -397,6 +399,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
+	int checkout_index;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -422,9 +425,26 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
 		    opts->new_branch);
 
-	if (opts->patch_mode)
-		return run_add_interactive(revision, "--patch=checkout",
-					   &opts->pathspec);
+	if (!opts->checkout_worktree && !opts->checkout_index)
+		die(_("neither '%s' or '%s' is specified"),
+		    "--staged", "--worktree");
+
+	if (!opts->checkout_worktree && !opts->from_treeish)
+		die(_("'%s' must be used when '%s' is not specified"),
+		    "--worktree", "--source");
+
+	if (opts->patch_mode) {
+		const char *patch_mode;
+
+		if (opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=checkout";
+		else if (opts->checkout_index && !opts->checkout_worktree)
+			patch_mode = "--patch=reset";
+		else
+			die(_("'%s' with only '%s' is not currently supported"),
+			    "--patch", "--worktree");
+		return run_add_interactive(revision, patch_mode, &opts->pathspec);
+	}
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(&opts->pathspec) < 0)
@@ -482,10 +502,30 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	errs |= checkout_worktree(opts);
+	if (opts->checkout_worktree)
+		errs |= checkout_worktree(opts);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-		die(_("unable to write new index file"));
+	/*
+	 * Allow updating the index when checking out from the index.
+	 * This is to save new stat info.
+	 */
+	if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+		checkout_index = 1;
+	else
+		checkout_index = opts->checkout_index;
+
+	if (checkout_index) {
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die(_("unable to write new index file"));
+	} else {
+		/*
+		 * NEEDSWORK: if --worktree is not specified, we
+		 * should save stat info of checked out files in the
+		 * index to avoid the next (potentially costly)
+		 * refresh. But it's a bit tricker to do...
+		 */
+		rollback_lock_file(&lock_file);
+	}
 
 	read_ref_full("HEAD", 0, &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -1461,6 +1501,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
+	if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = 0;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = 0;
+	} else {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = -opts->checkout_index - 1;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = -opts->checkout_worktree - 1;
+	}
+	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+		BUG("these flags should be non-negative by now");
+
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
 	 * and new_branch_force and new_orphan_branch will tell us which one of
@@ -1617,6 +1671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.orphan_from_empty_tree = 0;
 	opts.empty_pathspec_ok = 1;
 	opts.overlay_mode = -1;
+	opts.checkout_index = -2;    /* default on */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1674,6 +1730,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL('S', "staged", &opts.checkout_index,
+			   N_("restore the index")),
+		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+			   N_("restore the working tree (default)")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1684,6 +1744,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
 	opts.overlay_mode = 0;
+	opts.checkout_index = -1;    /* default off */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 07/16] restore: reject invalid combinations with --staged
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (5 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
                     ` (11 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

git-checkout rejects plenty of invalid option combinations. Since
git-checkout is equivalent of either

    git restore --source --staged --worktree

or

    git restore --worktree

that still leaves the new mode 'git restore --index' unprotected. Reject
some more invalid option combinations.

The other new mode 'restore --source --worktree' does not need anything
else.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index e855c64cfe..71e2589340 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -433,6 +433,16 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("'%s' must be used when '%s' is not specified"),
 		    "--worktree", "--source");
 
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->writeout_stage)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--ours", "--theirs", "--staged");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->merge)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--merge", "--conflict", "--staged");
+
 	if (opts->patch_mode) {
 		const char *patch_mode;
 
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 08/16] restore: default to --source=HEAD when only --staged is specified
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (6 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
                     ` (10 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

"git restore --staged" without --source does not make much sense since
by default we restore from the index.  Instead of copying the index to
itself, set the default source to HEAD in this case, yielding behavior
that matches "git reset -- <paths>".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e2589340..09a03f1ff8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1524,6 +1524,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	}
 	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
 		BUG("these flags should be non-negative by now");
+	/*
+	 * convenient shortcut: "git restore --staged" equals
+	 * "git restore --staged --source HEAD"
+	 */
+	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+		opts->from_treeish = "HEAD";
 
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 09/16] restore: replace --force with --ignore-unmerged
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (7 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
                     ` (9 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Use a more specific option name to express its purpose. --force may come
back as an alias of --ignore-unmerged and possibly more. But since this
is a destructive operation, I don't see why we need to "force" anything
more. We already don't hold back.

When 'checkout --force' or 'restore --ignore-unmerged' is used, we may
also print warnings about unmerged entries being ignore. Since this is
not exactly warning (people tell us to do so), more informational, let
it be suppressed if --quiet is given. This is a behavior change for
git-checkout.

PS. The diff looks a bit iffy since --force is moved to
add_common_switch_branch_options() (i.e. for switching). But
git-checkout is also doing switching and inherits this --force.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 09a03f1ff8..824ab65886 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -68,6 +68,8 @@ struct checkout_opts {
 	int empty_pathspec_ok;
 	int checkout_index;
 	int checkout_worktree;
+	const char *ignore_unmerged_opt;
+	int ignore_unmerged;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->new_branch_log)
 		die(_("'%s' cannot be used with updating paths"), "-l");
 
-	if (opts->force && opts->patch_mode)
-		die(_("'%s' cannot be used with updating paths"), "-f");
+	if (opts->ignore_unmerged && opts->patch_mode)
+		die(_("'%s' cannot be used with updating paths"),
+		    opts->ignore_unmerged_opt);
 
 	if (opts->force_detach)
 		die(_("'%s' cannot be used with updating paths"), "--detach");
@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->merge && opts->patch_mode)
 		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
 
-	if (opts->force && opts->merge)
-		die(_("'%s' cannot be used with %s"), "-f", "-m");
+	if (opts->ignore_unmerged && opts->merge)
+		die(_("'%s' cannot be used with %s"),
+		    opts->ignore_unmerged_opt, "-m");
 
 	if (opts->new_branch)
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
@@ -495,8 +499,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
-			if (opts->force) {
-				warning(_("path '%s' is unmerged"), ce->name);
+			if (opts->ignore_unmerged) {
+				if (!opts->quiet)
+					warning(_("path '%s' is unmerged"), ce->name);
 			} else if (opts->writeout_stage) {
 				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
 			} else if (opts->merge) {
@@ -1414,8 +1419,6 @@ static struct option *add_common_options(struct checkout_opts *opts,
 			    "checkout", "control recursive updating of submodules",
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
 		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
 		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
 		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
@@ -1433,6 +1436,8 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
@@ -1502,8 +1507,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
-	if (opts->force)
+	if (opts->force) {
 		opts->discard_changes = 1;
+		opts->ignore_unmerged_opt = "--force";
+		opts->ignore_unmerged = 1;
+	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1750,6 +1758,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 			   N_("restore the index")),
 		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
 			   N_("restore the working tree (default)")),
+		OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+			 N_("ignore unmerged entries")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1762,6 +1772,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.overlay_mode = 0;
 	opts.checkout_index = -1;    /* default off */
 	opts.checkout_worktree = -2; /* default on */
+	opts.ignore_unmerged_opt = "--ignore-unmerged";
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 10/16] restore: support --patch
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (8 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
                     ` (8 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

git-restore is different from git-checkout that it only restores the
worktree by default, not both worktree and index. add--interactive
needs some update to support this mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c        |  6 +++--
 git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 824ab65886..bed79ae595 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -454,9 +454,11 @@ static int checkout_paths(const struct checkout_opts *opts,
 			patch_mode = "--patch=checkout";
 		else if (opts->checkout_index && !opts->checkout_worktree)
 			patch_mode = "--patch=reset";
+		else if (!opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=worktree";
 		else
-			die(_("'%s' with only '%s' is not currently supported"),
-			    "--patch", "--worktree");
+			BUG("either flag must have been set, worktree=%d, index=%d",
+			    opts->checkout_worktree, opts->checkout_index);
 		return run_add_interactive(revision, patch_mode, &opts->pathspec);
 	}
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 20eb81cc92..3dfb3629c9 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -149,6 +149,20 @@ sub colored {
 		FILTER => undef,
 		IS_REVERSE => 0,
 	},
+	'worktree_head' => {
+		DIFF => 'diff-index -p',
+		APPLY => sub { apply_patch 'apply -R', @_ },
+		APPLY_CHECK => 'apply -R',
+		FILTER => undef,
+		IS_REVERSE => 1,
+	},
+	'worktree_nothead' => {
+		DIFF => 'diff-index -R -p',
+		APPLY => sub { apply_patch 'apply', @_ },
+		APPLY_CHECK => 'apply',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
 );
 
 $patch_mode = 'stage';
@@ -1049,6 +1063,12 @@ sub color_diff {
 marked for discarding."),
 	checkout_nothead => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+	worktree_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+	worktree_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
 marked for applying."),
 );
 
@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {
 n - do not apply this hunk to index and worktree
 q - quit; do not apply this hunk or any of the remaining ones
 a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+	worktree_head => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+	worktree_nothead => N__(
+"y - apply this hunk to worktree
+n - do not apply this hunk to worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
 d - do not apply this hunk or any of the later hunks in the file"),
 );
 
@@ -1421,6 +1453,16 @@ sub display_hunks {
 		deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
 		hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
 	},
+	worktree_head => {
+		mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+	},
+	worktree_nothead => {
+		mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+	},
 );
 
 sub patch_update_file {
@@ -1756,6 +1798,16 @@ sub process_args {
 						       'checkout_head' : 'checkout_nothead');
 					$arg = shift @ARGV or die __("missing --");
 				}
+			} elsif ($1 eq 'worktree') {
+				$arg = shift @ARGV or die __("missing --");
+				if ($arg eq '--') {
+					$patch_mode = 'checkout_index';
+				} else {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'worktree_head' : 'worktree_nothead');
+					$arg = shift @ARGV or die __("missing --");
+				}
 			} elsif ($1 eq 'stage' or $1 eq 'stash') {
 				$patch_mode = $1;
 				$arg = shift @ARGV or die __("missing --");
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 11/16] t: add tests for restore
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (9 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 12/16] completion: support restore Nguyễn Thái Ngọc Duy
                     ` (7 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/lib-patch-mode.sh               |  12 ++++
 t/t2070-restore.sh (new +x)       |  99 +++++++++++++++++++++++++++
 t/t2071-restore-patch.sh (new +x) | 110 ++++++++++++++++++++++++++++++
 3 files changed, 221 insertions(+)

diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 06c3c91762..cfd76bf987 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,28 +2,40 @@
 
 . ./test-lib.sh
 
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
 set_state () {
 	echo "$3" > "$1" &&
 	git add "$1" &&
 	echo "$2" > "$1"
 }
 
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
 save_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	cat "$1" > _worktree_"$noslash" &&
 	git show :"$1" > _index_"$noslash"
 }
 
+# set_and_save_state <path> <worktree-content> <index-content>
 set_and_save_state () {
 	set_state "$@" &&
 	save_state "$1"
 }
 
+# verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
 	test "$(cat "$1")" = "$2" &&
 	test "$(git show :"$1")" = "$3"
 }
 
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
 verify_saved_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
new file mode 100755
index 0000000000..73ea13ede9
--- /dev/null
+++ b/t/t2070-restore.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='restore basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	echo first-and-a-half >>first.t &&
+	git add first.t &&
+	test_commit second &&
+	echo one >one &&
+	echo two >two &&
+	echo untracked >untracked &&
+	echo ignored >ignored &&
+	echo /ignored >.gitignore &&
+	git add one two .gitignore &&
+	git update-ref refs/heads/one master
+'
+
+test_expect_success 'restore without pathspec is not ok' '
+	test_must_fail git restore &&
+	test_must_fail git restore --source=first
+'
+
+test_expect_success 'restore a file, ignoring branch of same name' '
+	cat one >expected &&
+	echo dirty >>one &&
+	git restore one &&
+	test_cmp expected one
+'
+
+test_expect_success 'restore a file on worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first first.t &&
+	test_cmp expected first.t &&
+	git cat-file blob HEAD:./first.t >expected &&
+	git show :first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore a file in the index from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	git cat-file blob HEAD:./first.t >expected &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore a file in both the index and worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged --worktree first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore --staged uses HEAD as source' '
+	test_when_finished git reset --hard &&
+	git cat-file blob :./first.t >expected &&
+	echo index-dirty >>first.t &&
+	git add first.t &&
+	git restore --staged first.t &&
+	git cat-file blob :./first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
+	git init unmerged &&
+	(
+		cd unmerged &&
+		echo one >unmerged &&
+		echo one >common &&
+		git add unmerged common &&
+		git commit -m common &&
+		git switch -c first &&
+		echo first >unmerged &&
+		git commit -am first &&
+		git switch -c second master &&
+		echo second >unmerged &&
+		git commit -am second &&
+		test_must_fail git merge first &&
+
+		echo dirty >>common &&
+		test_must_fail git restore . &&
+
+		git restore --ignore-unmerged --quiet . >output 2>&1 &&
+		git diff common >diff-output &&
+		: >empty &&
+		test_cmp empty output &&
+		test_cmp empty diff-output
+	)
+'
+
+test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
new file mode 100755
index 0000000000..98b2476e7c
--- /dev/null
+++ b/t/t2071-restore-patch.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='git restore --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent >dir/foo &&
+	echo dummy >bar &&
+	git add bar dir/foo &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+test_expect_success PERL 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n n | git restore -p &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git restore -p' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git restore -p with staged changes' '
+	set_state dir/foo work index &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD^' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD^ &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent index
+'
+
+test_expect_success PERL 'git restore -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent head
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | (cd dir && git restore -p foo) &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 12/16] completion: support restore
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (10 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
                     ` (6 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Completion for restore is straightforward. We could still do better
though by giving the list of just tracked files instead of all present
ones. But let's leave it for later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b24bc48276..58d18d41a2 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2491,6 +2491,21 @@ _git_reset ()
 	__git_complete_refs
 }
 
+_git_restore ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--source=*)
+		__git_complete_refs --cur="${cur##--source=}"
+		;;
+	--*)
+		__gitcomp_builtin restore
+		;;
+	esac
+}
+
 __git_revert_inprogress_options="--continue --quit --abort"
 
 _git_revert ()
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard'
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (11 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 12/16] completion: support restore Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
                     ` (5 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

Since the operation in progress is merge, stick to the 'git merge'
variant of aborting. 'git reset --hard' does not really tell you about
aborting the merge by just looking, longer to type, and even though I
know by heart what --hard do, I still dislike it when I need to consider
whether --hard, --mixed or --soft.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/user-manual.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 94799faa2b..4e210970e1 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess
 away, you can always return to the pre-merge state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git merge --abort
 -------------------------------------------------
 
 Or, if you've already committed the merge that you want to throw away,
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 14/16] doc: promote "git restore"
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (12 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 15/16] rm: add --staged as alias for --cached Nguyễn Thái Ngọc Duy
                     ` (4 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

The new command "git restore" (together with "git switch") are added
to avoid the confusion of one-command-do-all "git checkout" for new
users. They are also helpful to avoid ambiguous context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands.

One nice thing about git-restore is the ability to restore
"everything", so it can be used in "git status" advice instead of both
"git checkout" and "git reset".  The three commands suggested by "git
status" are add, rm and restore.

"git checkout" is also removed from "git help" (i.e. it's no longer
considered a commonly used command)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clean.txt        |  2 +-
 Documentation/git-commit.txt       |  2 +-
 Documentation/git-format-patch.txt |  2 +-
 Documentation/git-reset.txt        |  6 +--
 Documentation/git-revert.txt       |  4 +-
 Documentation/gitcli.txt           |  4 +-
 Documentation/giteveryday.txt      |  5 +-
 Documentation/gittutorial-2.txt    |  4 +-
 Documentation/gittutorial.txt      |  2 +-
 Documentation/user-manual.txt      | 12 ++---
 builtin/clone.c                    |  2 +-
 builtin/commit.c                   |  2 +-
 command-list.txt                   |  2 +-
 t/t7508-status.sh                  | 82 +++++++++++++++---------------
 t/t7512-status-help.sh             | 20 ++++----
 wt-status.c                        | 26 +++++++---
 16 files changed, 93 insertions(+), 84 deletions(-)

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 03056dad0d..8a31ccffea 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -64,7 +64,7 @@ OPTIONS
 	directory) and $GIT_DIR/info/exclude, but do still use the ignore
 	rules given with `-e` options.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
-	conjunction with 'git reset') to create a pristine
+	conjunction with 'git restore' or 'git reset') to create a pristine
 	working directory to test a clean build.
 
 -X::
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a85c2c2a4c..7628193284 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD -- <file>`,
+to that of the last commit with `git restore --staged <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 0a24a5679e..01bfcecf69 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:
 
     $ git fetch <project> master:test-apply
     $ git switch test-apply
-    $ git reset --hard
+    $ git restore --source=HEAD --staged --worktree :/
     $ git am a.patch
 
 If it does not apply correctly, there can be various reasons.
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index c25f8a95b9..633d71d36a 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -29,9 +29,9 @@ This means that `git reset <paths>` is the opposite of `git add
 `git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
-use linkgit:git-checkout[1] to check the contents out of the index to
-the working tree.
-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+use linkgit:git-restore[1] to check the contents out of the index to
+the working tree. Alternatively, using linkgit:git-restore[1]
+and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 018ecf49d3..9aadc36881 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the `--hard` option.  If
 you want to extract specific files as they were in another commit, you
-should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> -- <filename>` syntax.  Take care with these alternatives as
+should see linkgit:git-restore[1], specifically the `--source`
+option. Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 See "Reset, restore and revert" in linkgit:git[1] for the differences
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 592e06d839..e2a9674297 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.
    things:
 +
 --------------------------------
-$ git checkout -- *.c
-$ git checkout -- \*.c
+$ git restore *.c
+$ git restore \*.c
 --------------------------------
 +
 The former lets your shell expand the fileglob, and you are asking
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index ad455f3e39..1bd919f92b 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -51,8 +51,7 @@ following commands.
 
   * linkgit:git-commit[1] to advance the current branch.
 
-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with
-    pathname parameters) to undo changes.
+  * linkgit:git-restore[1] to undo changes.
 
   * linkgit:git-merge[1] to merge between local branches.
 
@@ -82,7 +81,7 @@ Create a topic branch and develop.::
 ------------
 $ git switch -c alsa-audio <1>
 $ edit/compile/test
-$ git checkout -- curses/ux_audio_oss.c <2>
+$ git restore curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
 $ edit/compile/test
 $ git diff HEAD <4>
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index e0976f6017..8bdb7d0bd3 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -370,13 +370,13 @@ situation:
 $ git status
 On branch master
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   closing.txt
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   file.txt
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index e6ad6b5f8d..59ef5cef1f 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -110,7 +110,7 @@ $ git status
 On branch master
 Changes to be committed:
 Your branch is up to date with 'origin/master'.
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   file1
 	modified:   file2
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 4e210970e1..8bce75b2cf 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed
 state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git restore --staged --worktree :/
 -------------------------------------------------
 
 If you make a commit that you later wish you hadn't, there are two
@@ -1523,12 +1523,10 @@ Checking out an old version of a file
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used `git checkout` before to switch
-branches, but it has quite different behavior if it is given a path
-name: the command
+linkgit:git-restore[1]. The command
 
 -------------------------------------------------
-$ git checkout HEAD^ path/to/file
+$ git restore --source=HEAD^ path/to/file
 -------------------------------------------------
 
 replaces path/to/file by the contents it had in the commit HEAD^, and
@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.
 The Workflow
 ------------
 
-High-level operations such as linkgit:git-commit[1],
-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data
+High-level operations such as linkgit:git-commit[1] and
+linkgit:git-restore[1] work by moving data
 between the working tree, the index, and the object database.  Git
 provides low-level operations which perform each of these steps
 individually.
diff --git a/builtin/clone.c b/builtin/clone.c
index 50bde99618..024eb57996 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -492,7 +492,7 @@ static enum {
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry with 'git restore --source=HEAD :/'\n");
 
 static void remove_junk(void)
 {
diff --git a/builtin/commit.c b/builtin/commit.c
index f17537474a..fa5982cc86 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
-		      "not exceeded, and then \"git reset HEAD\" to recover."));
+		      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
 	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 		write_commit_graph_reachable(get_object_directory(), 0, 0);
diff --git a/command-list.txt b/command-list.txt
index b9eae1c258..cf8dccb439 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
 git-check-mailmap                       purehelpers
-git-checkout                            mainporcelain           history
+git-checkout                            mainporcelain
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
 git-cherry                              plumbinginterrogators          complete
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e1f11293e2..681bc314b4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -94,13 +94,13 @@ test_expect_success 'status --column' '
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -128,13 +128,13 @@ cat >expect <<\EOF
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   ../dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   modified
 
@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	<GREEN>new file:   dir2/added<RESET>
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	<RED>modified:   dir1/modified<RESET>
 
@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   dir1/modified
 
@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD^1 <file>..." to unstage)
+  (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
   (commit or discard the untracked or modified content in submodules)
 
 	modified:   dir1/modified
@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 	modified:   sm (new commits)
@@ -1379,13 +1379,13 @@ cat > expect << EOF
 ;   (use "git pull" to merge the remote branch into yours)
 ;
 ; Changes to be committed:
-;   (use "git reset HEAD <file>..." to unstage)
+;   (use "git restore --staged <file>..." to unstage)
 ;
 ;	modified:   sm
 ;
 ; Changes not staged for commit:
 ;   (use "git add <file>..." to update what will be committed)
-;   (use "git checkout -- <file>..." to discard changes in working directory)
+;   (use "git restore <file>..." to discard changes in working directory)
 ;
 ;	modified:   dir1/modified
 ;	modified:   sm (new commits)
@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 458608cc1e..1b9712c675 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -85,7 +85,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -110,7 +110,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -148,7 +148,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -176,7 +176,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\''split_commit'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   to-revert.txt
@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   to-revert.txt
 
diff --git a/wt-status.c b/wt-status.c
index 445a36204a..19fd1add75 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		;
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 
 	if (!both_deleted) {
@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c
+					 , _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,
 		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
 	else
 		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
-	status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+	status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
 		status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
 	status_printf_ln(s, c, "%s", "");
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 15/16] rm: add --staged as alias for --cached
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (13 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-11 13:12   ` [PATCH v2 16/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
                     ` (3 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

After the last patch, "git status" now suggests

- add                  to stage some changes
- restore [--worktree] to undo worktree changes
- restore --staged     to undo index changes
- rm --cached          to remove files from the index

This change is to make the suggestions for more consistent by using
--staged across all suggested commands instead of the
still-a-bit-hard-to-understand --cached.

PS. Should we suggest "git stage" instead of "git add"? Maybe that's
going too far?

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-rm.txt | 7 ++++---
 builtin/rm.c             | 7 ++++---
 t/t3600-rm.sh            | 6 +++---
 t/t7508-status.sh        | 2 +-
 wt-status.c              | 4 ++--
 5 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b5c46223c4..4271fc5eaa 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,7 +8,7 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
+'git rm' [-f | --force] [-n] [-r] [--staged] [--ignore-unmatch] [--quiet] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -55,10 +55,11 @@ OPTIONS
 	the list of files, (useful when filenames might be mistaken
 	for command-line options).
 
+--staged::
 --cached::
 	Use this option to unstage and remove paths only from the index.
-	Working tree files, whether modified or not, will be
-	left alone.
+	Working tree files, whether modified or not, will be left
+	alone. `--cached` is synonym for `--staged`.
 
 --ignore-unmatch::
 	Exit with a zero status even if no files matched.
diff --git a/builtin/rm.c b/builtin/rm.c
index db85b33982..47c8eb100b 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -217,7 +217,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 			     "staged in the index:",
 			     "the following files have changes "
 			     "staged in the index:", files_cached.nr),
-			  _("\n(use --cached to keep the file,"
+			  _("\n(use --staged to keep the file,"
 			    " or -f to force removal)"),
 			  &errs);
 	string_list_clear(&files_cached, 0);
@@ -226,7 +226,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 			  Q_("the following file has local modifications:",
 			     "the following files have local modifications:",
 			     files_local.nr),
-			  _("\n(use --cached to keep the file,"
+			  _("\n(use --staged to keep the file,"
 			    " or -f to force removal)"),
 			  &errs);
 	string_list_clear(&files_local, 0);
@@ -240,7 +240,8 @@ static int ignore_unmatch = 0;
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only, N_("dry run")),
 	OPT__QUIET(&quiet, N_("do not list removed files")),
-	OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),
+	OPT_BOOL( 0 , "staged",         &index_only, N_("only remove from the index")),
+	OPT_BOOL( 0 , "cached",         &index_only, N_("synonym for --staged")),
 	OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE),
 	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
 	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 04e5d42bd3..5686032b8c 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -797,7 +797,7 @@ test_expect_success 'rm file with local modification' '
 	cat >expect <<-\EOF &&
 	error: the following file has local modifications:
 	    foo.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	git commit -m "testing rm 3" &&
 	echo content3 >foo.txt &&
@@ -819,7 +819,7 @@ test_expect_success 'rm file with changes in the index' '
 	cat >expect <<-\EOF &&
 	error: the following file has changes staged in the index:
 	    foo.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	git reset --hard &&
 	echo content5 >foo.txt &&
@@ -845,7 +845,7 @@ test_expect_success 'rm files with two different errors' '
 	(use -f to force removal)
 	error: the following file has changes staged in the index:
 	    bar1.txt
-	(use --cached to keep the file, or -f to force removal)
+	(use --staged to keep the file, or -f to force removal)
 	EOF
 	echo content >foo1.txt &&
 	git add foo1.txt &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 681bc314b4..738f3df2f9 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -71,7 +71,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'status (1)' '
-	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+	test_i18ngrep "use \"git rm --staged <file>\.\.\.\" to unstage" output
 '
 
 strip_comments () {
diff --git a/wt-status.c b/wt-status.c
index 19fd1add75..4d065ce89e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -187,7 +187,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
 					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 					 s->reference);
 	} else
-		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+		status_printf_ln(s, c, _("  (use \"git rm --staged <file>...\" to unstage)"));
 
 	if (!both_deleted) {
 		if (!del_mod_conflict)
@@ -220,7 +220,7 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
 					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 					 s->reference);
 	} else
-		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+		status_printf_ln(s, c, _("  (use \"git rm --staged <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
 
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v2 16/16] help: move git-diff and git-reset to different groups
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (14 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 15/16] rm: add --staged as alias for --cached Nguyễn Thái Ngọc Duy
@ 2019-04-11 13:12   ` Nguyễn Thái Ngọc Duy
  2019-04-12  5:14   ` [PATCH v2 00/16] Add new command 'restore' Junio C Hamano
                     ` (2 subsequent siblings)
  18 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-11 13:12 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, newren, Eric Sunshine, rybak.a.v,
	Johannes Schindelin, jacob.keller

The third column in command-list.txt determines what group a common
command is printed in 'git help'.

"git reset" is currently in the "work on the current change (see also:
git help everyday)" group. While it's true that "git reset" can
manipulate the index and can be in this group, its unique
functionality is resetting HEAD, which should be the "grow, mark,
tweak history" group.

Moving it there will also avoid the confusion because both 'restore'
and 'reset' are in the same group, next to each other.

While looking at the 'group, mark, tweak history', I realize "git
diff" should not be there. All the commands in this group is about
_changing_ the commit history while "git diff" is a read-only
operation. It fits better in the "examine the history and state" group
(especially when "git status", its close friend, is already there).

This is what we have after the reorganization:

    work on the current change (see also: git help everyday)
       add       Add file contents to the index
       mv        Move or rename a file, a directory, or a symlink
       restore   Restore working tree files
       rm        Remove files from the working tree and from the index

    examine the history and state (see also: git help revisions)
       bisect    Use binary search to find the commit that introduced a bug
       diff      Show changes between commits, commit and working tree, etc
       grep      Print lines matching a pattern
       log       Show commit logs
       show      Show various types of objects
       status    Show the working tree status

    grow, mark and tweak your common history
       branch    List, create, or delete branches
       commit    Record changes to the repository
       merge     Join two or more development histories together
       rebase    Reapply commits on top of another base tip
       reset     Reset current HEAD to the specified state
       switch    Switch branches
       tag       Create, list, delete or verify a tag object signed with GPG

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 command-list.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/command-list.txt b/command-list.txt
index cf8dccb439..a9ac72bef4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
 git-daemon                              synchingrepositories
 git-describe                            mainporcelain
-git-diff                                mainporcelain           history
+git-diff                                mainporcelain           info
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
-git-reset                               mainporcelain           worktree
+git-reset                               mainporcelain           history
 git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
-- 
2.21.0.682.g30d2204636


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (15 preceding siblings ...)
  2019-04-11 13:12   ` [PATCH v2 16/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
@ 2019-04-12  5:14   ` Junio C Hamano
  2019-04-13 10:39     ` Duy Nguyen
  2019-04-17 10:04   ` Duy Nguyen
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
  18 siblings, 1 reply; 97+ messages in thread
From: Junio C Hamano @ 2019-04-12  5:14 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, newren, Eric Sunshine, rybak.a.v, Johannes Schindelin, jacob.keller

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> v2 should address all the comments from v1. I still haven't done that
> --intent-to-add thing yet even though I'd like to make it default
> behavior too. Anyway changes are
>
> - --index is renamed to --staged to avoid conflict with --index from
>   git-apply, which has different meaning.

I think this is a reasonable compromise.  Documentation/gitcli.txt
needs to be updated for this new addition, where it currently only
talks about the distinction between "--cached" and "--index".

> - git-rm learns about --staged as an alias of --cached (in fact it's
>   more the other way around). This is to keep suggestions consistent
>   because we tell people to do "git foo --staged" everywhere.

I am not sure 100% about this one.  If "--staged" is only about
updating the index without touching the working tree, then existing
"--cached" is already serving its purpose and there is no need to
add yet another.  Why did we need it for "restore-paths" in the
first place?  Is it because the target of restoring can be one of
the three (index+working-tree, index-only, or working-tree only),
not just the traditional two (index+working-tree that is signalled
by "--index", and index-only that is signalled by "--cached")?

I think it would make sense to introduce --staged to "git rm", if we
teach the third target (i.e. "working-tree only") to the command,
but otherwise, I am not sure if it would help reducing conflusion.

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-12  5:14   ` [PATCH v2 00/16] Add new command 'restore' Junio C Hamano
@ 2019-04-13 10:39     ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-04-13 10:39 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Elijah Newren, Eric Sunshine, Andrei Rybak,
	Johannes Schindelin, Jacob Keller

On Fri, Apr 12, 2019 at 12:14 PM Junio C Hamano <gitster@pobox.com> wrote:
> > - git-rm learns about --staged as an alias of --cached (in fact it's
> >   more the other way around). This is to keep suggestions consistent
> >   because we tell people to do "git foo --staged" everywhere.
>
> I am not sure 100% about this one.  If "--staged" is only about
> updating the index without touching the working tree, then existing
> "--cached" is already serving its purpose and there is no need to
> add yet another.

It's so we suggest the same option name in "git status" instead of

 - git add/rm/retore <paths>
 - git restore [--source] --staged <paths>
 - git rm --cached <paths>

It's in the name of consistency but perhaps it's just not worth it? It
could also be seen as the continuation of adding --staged [1]

[1] https://public-inbox.org/git/1225296936-1357-1-git-send-email-dsymonds@gmail.com/

> Why did we need it for "restore-paths" in the
> first place?  Is it because the target of restoring can be one of
> the three (index+working-tree, index-only, or working-tree only),
> not just the traditional two (index+working-tree that is signalled
> by "--index", and index-only that is signalled by "--cached")?

Yes.

> I think it would make sense to introduce --staged to "git rm", if we
> teach the third target (i.e. "working-tree only") to the command,
> but otherwise, I am not sure if it would help reducing conflusion.

I could add that. "git rm --worktree" then is almost like "rm" except
that only tracked paths are affected. The default target of "git rm"
is still "both worktree and index" though. I don't think we can ever
change that (e.g. to match "git restore" which defaults to worktree
only)
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (16 preceding siblings ...)
  2019-04-12  5:14   ` [PATCH v2 00/16] Add new command 'restore' Junio C Hamano
@ 2019-04-17 10:04   ` Duy Nguyen
  2019-04-18  0:38     ` Junio C Hamano
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
  18 siblings, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-04-17 10:04 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Elijah Newren, Eric Sunshine,
	Andrei Rybak, Johannes Schindelin, Jacob Keller

On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> This is the companion of "git switch" and is based on that topic.
> This command peforms the "checkout paths" from git-checkout, git-reset
> and also has a third mode to reset only worktree, leaving the index
> alone.

It does not have to be done now. But I'm just wondering, does anyone
think adding --dry-run is a good idea? This command is destructive by
default, so careful people might want it, I dunno.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-17 10:04   ` Duy Nguyen
@ 2019-04-18  0:38     ` Junio C Hamano
  2019-04-18  9:40       ` Duy Nguyen
  2019-04-18 10:03       ` Johannes Schindelin
  0 siblings, 2 replies; 97+ messages in thread
From: Junio C Hamano @ 2019-04-18  0:38 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Elijah Newren, Eric Sunshine, Andrei Rybak,
	Johannes Schindelin, Jacob Keller

Duy Nguyen <pclouds@gmail.com> writes:

> On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>
>> This is the companion of "git switch" and is based on that topic.
>> This command peforms the "checkout paths" from git-checkout, git-reset
>> and also has a third mode to reset only worktree, leaving the index
>> alone.
>
> It does not have to be done now. But I'm just wondering, does anyone
> think adding --dry-run is a good idea? This command is destructive by
> default, so careful people might want it, I dunno.

Yeah, "give --dry-run for anything potentially destructive" may be a
good general principle, although we'd need to know where to stop.
For example, I am not sure if "git reset --hard -n" would make all
that much sense, as the sole point of running "reset --hard" is "I
made an unrecoverable mess---get me back to a clean and known state
no matter what" and in that context, "let me see which files in the
working tree will be removed and checked out of the tree-ish and
which paths in the index records a different blob than what is in
the tree-ish, before deciding if I still want to stay in the
unrecoverable mess or I want to get out of it" is something you
could request, but at the same time, there is not much point in
actually asking it.  Of course, a --dry-run that is not used often
simply because it is not all that useful in practice is fine to have
as long as it works correctly---I am saying that it's not something
I'd personally prioritize myself.

It would be an excellent addition to "restore-path" (and also to
"checkout [<tree-ish> [--]] pathspec") to give "--dry-run".  Not
just because it is destructive, but because unlike "reset --hard",
it is selectively destructive.  Having a way to make sure that the
given pathspec touches only the paths that the user intends to
recover from the tree-ish or the index would be valuable.

But it is a new feature, and I'd think it can (and probably should)
be done as a follow-on series outside the main series you have been
working on.  Let's make sure that we have the basics ready by the
end of this cycle.

Thanks.




^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-18  0:38     ` Junio C Hamano
@ 2019-04-18  9:40       ` Duy Nguyen
  2019-04-18 10:03       ` Johannes Schindelin
  1 sibling, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-04-18  9:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Elijah Newren, Eric Sunshine, Andrei Rybak,
	Johannes Schindelin, Jacob Keller

On Thu, Apr 18, 2019 at 7:38 AM Junio C Hamano <gitster@pobox.com> wrote:
> It would be an excellent addition to "restore-path" (and also to
> "checkout [<tree-ish> [--]] pathspec") to give "--dry-run".  Not
> just because it is destructive, but because unlike "reset --hard",
> it is selectively destructive.  Having a way to make sure that the
> given pathspec touches only the paths that the user intends to
> recover from the tree-ish or the index would be valuable.

Yep. I thought about that complicated pathspec stuff but forgot to
mention. When you start using wildcards and magic, it's sometimes hard
to predict the outcome on the first try.

Also about git-checkout but that's kinda weird. git-checkout has two
faces, the switching business is "safety first" and --dry-run does not
make sense. The restoring paths can make use of --dry-run, but then
we'll need to describe that this option only applies to one of the two
cases, urghhh

> But it is a new feature, and I'd think it can (and probably should)
> be done as a follow-on series outside the main series you have been
> working on.  Let's make sure that we have the basics ready by the
> end of this cycle.

Yeah, it's not going to happen now. I will also drop "rm --staged" in
the next reroll to stop expanding the scope of this series. "rm
[--staged] [--worktree]" may come back, but now is not its time.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-18  0:38     ` Junio C Hamano
  2019-04-18  9:40       ` Duy Nguyen
@ 2019-04-18 10:03       ` Johannes Schindelin
  2019-04-18 10:20         ` Duy Nguyen
  1 sibling, 1 reply; 97+ messages in thread
From: Johannes Schindelin @ 2019-04-18 10:03 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Duy Nguyen, Git Mailing List, Elijah Newren, Eric Sunshine,
	Andrei Rybak, Jacob Keller

[-- Attachment #1: Type: text/plain, Size: 2807 bytes --]

Hi,

On Thu, 18 Apr 2019, Junio C Hamano wrote:

> Duy Nguyen <pclouds@gmail.com> writes:
>
> > On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> >>
> >> This is the companion of "git switch" and is based on that topic.
> >> This command peforms the "checkout paths" from git-checkout, git-reset
> >> and also has a third mode to reset only worktree, leaving the index
> >> alone.
> >
> > It does not have to be done now. But I'm just wondering, does anyone
> > think adding --dry-run is a good idea? This command is destructive by
> > default, so careful people might want it, I dunno.
>
> Yeah, "give --dry-run for anything potentially destructive" may be a
> good general principle, although we'd need to know where to stop.
> For example, I am not sure if "git reset --hard -n" would make all
> that much sense, as the sole point of running "reset --hard" is "I
> made an unrecoverable mess---get me back to a clean and known state
> no matter what" and in that context, "let me see which files in the
> working tree will be removed and checked out of the tree-ish and
> which paths in the index records a different blob than what is in
> the tree-ish, before deciding if I still want to stay in the
> unrecoverable mess or I want to get out of it" is something you
> could request, but at the same time, there is not much point in
> actually asking it.  Of course, a --dry-run that is not used often
> simply because it is not all that useful in practice is fine to have
> as long as it works correctly---I am saying that it's not something
> I'd personally prioritize myself.
>
> It would be an excellent addition to "restore-path" (and also to
> "checkout [<tree-ish> [--]] pathspec") to give "--dry-run".  Not
> just because it is destructive, but because unlike "reset --hard",
> it is selectively destructive.  Having a way to make sure that the
> given pathspec touches only the paths that the user intends to
> recover from the tree-ish or the index would be valuable.
>
> But it is a new feature, and I'd think it can (and probably should)
> be done as a follow-on series outside the main series you have been
> working on.  Let's make sure that we have the basics ready by the
> end of this cycle.

Since this command is supposed to make our not-quite-user-friendly
command-line interface a lot more user-friendly, I think that we should
take a step back and think very hard about a way to make this recoverable
an action.

The recently discussed project to make `git stash` handle unmerged index
entries should be a good step in that direction.

And I do not think that it would make a lot of sense to advance this
feature prematurely, i.e. without this safety hatch firmly in place.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v2 00/16] Add new command 'restore'
  2019-04-18 10:03       ` Johannes Schindelin
@ 2019-04-18 10:20         ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-04-18 10:20 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, Git Mailing List, Elijah Newren, Eric Sunshine,
	Andrei Rybak, Jacob Keller

On Thu, Apr 18, 2019 at 5:03 PM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> Hi,
>
> On Thu, 18 Apr 2019, Junio C Hamano wrote:
>
> > Duy Nguyen <pclouds@gmail.com> writes:
> >
> > > On Thu, Apr 11, 2019 at 8:12 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > >>
> > >> This is the companion of "git switch" and is based on that topic.
> > >> This command peforms the "checkout paths" from git-checkout, git-reset
> > >> and also has a third mode to reset only worktree, leaving the index
> > >> alone.
> > >
> > > It does not have to be done now. But I'm just wondering, does anyone
> > > think adding --dry-run is a good idea? This command is destructive by
> > > default, so careful people might want it, I dunno.
> >
> > Yeah, "give --dry-run for anything potentially destructive" may be a
> > good general principle, although we'd need to know where to stop.
> > For example, I am not sure if "git reset --hard -n" would make all
> > that much sense, as the sole point of running "reset --hard" is "I
> > made an unrecoverable mess---get me back to a clean and known state
> > no matter what" and in that context, "let me see which files in the
> > working tree will be removed and checked out of the tree-ish and
> > which paths in the index records a different blob than what is in
> > the tree-ish, before deciding if I still want to stay in the
> > unrecoverable mess or I want to get out of it" is something you
> > could request, but at the same time, there is not much point in
> > actually asking it.  Of course, a --dry-run that is not used often
> > simply because it is not all that useful in practice is fine to have
> > as long as it works correctly---I am saying that it's not something
> > I'd personally prioritize myself.
> >
> > It would be an excellent addition to "restore-path" (and also to
> > "checkout [<tree-ish> [--]] pathspec") to give "--dry-run".  Not
> > just because it is destructive, but because unlike "reset --hard",
> > it is selectively destructive.  Having a way to make sure that the
> > given pathspec touches only the paths that the user intends to
> > recover from the tree-ish or the index would be valuable.
> >
> > But it is a new feature, and I'd think it can (and probably should)
> > be done as a follow-on series outside the main series you have been
> > working on.  Let's make sure that we have the basics ready by the
> > end of this cycle.
>
> Since this command is supposed to make our not-quite-user-friendly
> command-line interface a lot more user-friendly, I think that we should
> take a step back and think very hard about a way to make this recoverable
> an action.

backup-log can handle that (or at least provide the foundation) and it
even allows to recover from accidental "git reset --hard". But since
I'm not going to resubmit that again (I'm done with thinking hard
about "git undo" or overwriting ignored files, I do not have the
answer), I'll let --dry-run rest in peace.

> The recently discussed project to make `git stash` handle unmerged index
> entries should be a good step in that direction.
>
> And I do not think that it would make a lot of sense to advance this
> feature prematurely, i.e. without this safety hatch firmly in place.
>
> Ciao,
> Dscho
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 00/16] Add new command 'restore'
  2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
                     ` (17 preceding siblings ...)
  2019-04-17 10:04   ` Duy Nguyen
@ 2019-04-25  9:45   ` " Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
                       ` (16 more replies)
  18 siblings, 17 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

v3 changes are really small

- gitcli.txt is updated to mention the --staged/--worktree pair, in
  comparison to --cached/--index which is also mention there.

- The patch 'rm --staged' is dropped. It could come back. But if it
  does, it should be in a series where commands that take
  --cached/--index will now take both --staged/--worktree. Those are
  'rm', 'apply', 'check-attr', 'grep', 'diff' and maybe 'ls-files'.
  
  In other words we support --staged/--worktree everywhere while
  --cached/--index still remains because of muscle memory. This is
  of course only a good move if --staged/--worktree is much better
  than --cached/--index.

- Since there's a chance this series may end up in 'master' and get
  released, and I'm still worried that I screwed up somewhere, the last
  patch declares the two commands experimental for maybe one or two
  release cycles.
  
  If the reception is good, we revert that patch. If something
  horrible is found, we can still change the default behavior without
  anybody yelling at us. Or worst case scenario, we remove both
  commands and declare a failed experiment.

PS. the intent-to-add support is still not in. But it should be in
before the experimental status is removed.

Nguyễn Thái Ngọc Duy (16):
  checkout: split part of it to new command 'restore'
  restore: take tree-ish from --source option instead
  restore: make pathspec mandatory
  restore: disable overlay mode by default
  checkout: factor out worktree checkout code
  restore: add --worktree and --staged
  restore: reject invalid combinations with --staged
  restore: default to --source=HEAD when only --staged is specified
  restore: replace --force with --ignore-unmerged
  restore: support --patch
  t: add tests for restore
  completion: support restore
  user-manual.txt: prefer 'merge --abort' over 'reset --hard'
  doc: promote "git restore"
  help: move git-diff and git-reset to different groups
  Declare both git-switch and git-restore experimental

 .gitignore                             |   1 +
 Documentation/config/interactive.txt   |   3 +-
 Documentation/git-checkout.txt         |   3 +-
 Documentation/git-clean.txt            |   2 +-
 Documentation/git-commit.txt           |   2 +-
 Documentation/git-format-patch.txt     |   2 +-
 Documentation/git-reset.txt            |  13 +-
 Documentation/git-restore.txt (new)    | 185 +++++++++++++++
 Documentation/git-revert.txt           |   7 +-
 Documentation/git-switch.txt           |   2 +
 Documentation/git.txt                  |  20 ++
 Documentation/gitcli.txt               |  16 +-
 Documentation/giteveryday.txt          |   5 +-
 Documentation/gittutorial-2.txt        |   4 +-
 Documentation/gittutorial.txt          |   2 +-
 Documentation/user-manual.txt          |  14 +-
 Makefile                               |   1 +
 builtin.h                              |   1 +
 builtin/checkout.c                     | 299 +++++++++++++++++++------
 builtin/clone.c                        |   2 +-
 builtin/commit.c                       |   2 +-
 command-list.txt                       |   7 +-
 contrib/completion/git-completion.bash |  15 ++
 git-add--interactive.perl              |  52 +++++
 git.c                                  |   1 +
 t/lib-patch-mode.sh                    |  12 +
 t/t2070-restore.sh (new +x)            |  99 ++++++++
 t/t2071-restore-patch.sh (new +x)      | 110 +++++++++
 t/t7508-status.sh                      |  82 +++----
 t/t7512-status-help.sh                 |  20 +-
 wt-status.c                            |  26 ++-
 31 files changed, 850 insertions(+), 160 deletions(-)
 create mode 100644 Documentation/git-restore.txt
 create mode 100755 t/t2070-restore.sh
 create mode 100755 t/t2071-restore-patch.sh

Range-diff dựa trên v2:
 1:  41788fc2d2 !  1:  0d5ea2b7fe checkout: split part of it to new command 'restore'
    @@ -342,6 +342,29 @@
      Low-level commands (plumbing)
      -----------------------------
     
    + diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
    + --- a/Documentation/gitcli.txt
    + +++ b/Documentation/gitcli.txt
    +@@
    + http://marc.info/?l=git&m=119150393620273 for further
    + information.
    + 
    ++Some other commands that also work on files in the working tree and/or
    ++in the index can take `--staged` and/or `--worktree`.
    ++
    ++* `--staged` is exactly like `--cached`, which is used to ask a
    ++  command to only work on the index, not the working tree.
    ++
    ++* `--worktree` is the opposite, to ask a command to work on the
    ++  working tree only, not the index.
    ++
    ++* The two options can be specified together to ask a command to work
    ++  on both the index and the working tree.
    ++
    + GIT
    + ---
    + Part of the linkgit:git[1] suite
    +
      diff --git a/Makefile b/Makefile
      --- a/Makefile
      +++ b/Makefile
 2:  253dfb42ae =  2:  c3c6e7762a restore: take tree-ish from --source option instead
 3:  0266c4d982 =  3:  0fbf963e7d restore: make pathspec mandatory
 4:  ae4dd4c24e =  4:  2509bebf32 restore: disable overlay mode by default
 5:  c51dd232c0 =  5:  206c507f7d checkout: factor out worktree checkout code
 6:  6b19ddb1b3 =  6:  656bfcd659 restore: add --worktree and --staged
 7:  3f1031cc23 =  7:  f2d3aa1027 restore: reject invalid combinations with --staged
 8:  d6d9bed95c =  8:  57efad405d restore: default to --source=HEAD when only --staged is specified
 9:  fca91f3cca =  9:  3c6b32c223 restore: replace --force with --ignore-unmerged
10:  079273e2df = 10:  6665d7523b restore: support --patch
11:  9d28d167fa = 11:  d7bda0c0cc t: add tests for restore
12:  acd490f8b0 = 12:  e4622aff3d completion: support restore
13:  336a7d8921 = 13:  787b0e485e user-manual.txt: prefer 'merge --abort' over 'reset --hard'
14:  3b81b27255 = 14:  0df020c2c8 doc: promote "git restore"
15:  ce7e890524 <  -:  ---------- rm: add --staged as alias for --cached
16:  763aa1d6f1 = 15:  3302b1b0e1 help: move git-diff and git-reset to different groups
 -:  ---------- > 16:  ffeea858a7 Declare both git-switch and git-restore experimental
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 01/16] checkout: split part of it to new command 'restore'
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` " Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
                       ` (15 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Previously the switching branch business of 'git checkout' becomes a
new command 'switch'. This adds the restore command for the checking
out paths path.

Similar to git-switch, a new man page is added to describe what the
command will become. The implementation will be updated shortly to
match the man page.

A couple main differences from 'git checkout <paths>':

- 'restore' by default will only update worktree. This matters more
  when --source is specified ('checkout <tree> <paths>' updates both
  worktree and index).

- 'restore --staged' can be used to restore the index. This command
  overlaps with 'git reset <paths>'.

- both worktree and index could also be restored at the same time
  (from a tree) when both --staged and --worktree are specified. This
  overlaps with 'git checkout <tree> <paths>'

- default source for restoring worktree and index is the index and
  HEAD respectively. A different (tree) source could be specified as
  with --source (*).

- when both index and worktree are restored, --source must be
  specified since the default source for these two individual targets
  are different (**)

- --no-overlay is enabled by default, if an entry is missing in the
  source, restoring means deleting the entry

(*) I originally went with --from instead of --source. I still think
  --from is a better name. The short option -f however is already
  taken by force. And I do think short option is good to have, e.g. to
  write -s@ or -s@^ instead of --source=HEAD.

(**) If you sit down and think about it, moving worktree's source from
  the index to HEAD makes sense, but nobody is really thinking it
  through when they type the commands.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                           |   1 +
 Documentation/config/interactive.txt |   3 +-
 Documentation/git-checkout.txt       |   3 +-
 Documentation/git-reset.txt          |   7 +-
 Documentation/git-restore.txt (new)  | 183 +++++++++++++++++++++++++++
 Documentation/git-revert.txt         |   3 +
 Documentation/git.txt                |  20 +++
 Documentation/gitcli.txt             |  12 ++
 Makefile                             |   1 +
 builtin.h                            |   1 +
 builtin/checkout.c                   |  26 ++++
 command-list.txt                     |   1 +
 git.c                                |   1 +
 13 files changed, 257 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index c687b92b1c..fb377106be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@
 /git-request-pull
 /git-rerere
 /git-reset
+/git-restore
 /git-rev-list
 /git-rev-parse
 /git-revert
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index ad846dd7c9..a2d3c7ec44 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -2,7 +2,8 @@ interactive.singleKey::
 	In interactive commands, allow the user to provide one-letter
 	input with a single key (i.e., without hitting enter).
 	Currently this is used by the `--patch` mode of
-	linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+	linkgit:git-add[1], linkgit:git-checkout[1],
+	linkgit:git-restore[1], linkgit:git-commit[1],
 	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
 	setting is silently ignored if portable keystroke input
 	is not available; requires the Perl module Term::ReadKey.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 58f18a0842..a294652dd6 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -570,7 +570,8 @@ $ git add frotz
 
 SEE ALSO
 --------
-linkgit:git-switch[1]
+linkgit:git-switch[1],
+linkgit:git-restore[1]
 
 GIT
 ---
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index cbf901efb4..c25f8a95b9 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -25,7 +25,8 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 	the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
-<paths>`.
+<paths>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
 use linkgit:git-checkout[1] to check the contents out of the index to
@@ -86,8 +87,8 @@ but carries forward unmerged index entries.
 	changes, reset is aborted.
 --
 
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
 
 
 OPTIONS
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
new file mode 100644
index 0000000000..b608f3f360
--- /dev/null
+++ b/Documentation/git-restore.txt
@@ -0,0 +1,183 @@
+git-restore(1)
+==============
+
+NAME
+----
+git-restore - Restore working tree files
+
+SYNOPSIS
+--------
+[verse]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+
+DESCRIPTION
+-----------
+Restore specified paths in the working tree with some contents from a
+restore source. If a path is tracked but does not exist in the restore
+source, it will be removed to match the source.
+
+The command can also be used to restore the content in the index with
+`--staged`, or restore both the working tree and the index with
+`--staged --worktree`.
+
+By default, the restore sources for working tree and the index are the
+index and `HEAD` respectively. `--source` could be used to specify a
+commit as the restore source.
+
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
+OPTIONS
+-------
+-s <tree>::
+--source=<tree>::
+	Restore the working tree files with the content from the given
+	tree. It is common to specify the source tree by naming a
+	commit, branch or tag associated with it.
++
+If not specified, the default restore source for the working tree is
+the index, and the default restore source for the index index is
+`HEAD`. When both `--staged` and `--worktree` are specified,
+`--source` must also be specified.
+
+-p::
+--patch::
+	Interactively select hunks in the difference between the
+	restore source and the restore location. See the ``Interactive
+	Mode'' section of linkgit:git-add[1] to learn how to operate
+	the `--patch` mode.
++
+Note that `--patch` can accept no pathspec and will prompt to restore
+all modified paths.
+
+-W::
+--worktree::
+-S::
+--staged::
+	Specify the restore location. If neither option is specified,
+	by default the working tree is restored. Specifying `--staged`
+	will only restore the index. Specifying both restores both.
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages. Implies `--no-progress`.
+
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless `--quiet`
+	is specified. This flag enables progress reporting even if not
+	attached to a terminal, regardless of `--quiet`.
+
+--ours::
+--theirs::
+	When restoring files in the working tree from the index, use
+	stage #2 ('ours') or #3 ('theirs') for unmerged paths.
++
+Note that during `git rebase` and `git pull --rebase`, 'ours' and
+'theirs' may appear swapped. See the explanation of the same options
+in linkgit:git-checkout[1] for details.
+
+-m::
+--merge::
+	When restoring files on the working tree from the index,
+	recreate the conflicted merge in the unmerged paths.
+
+--conflict=<style>::
+	The same as `--merge` option above, but changes the way the
+	conflicting hunks are presented, overriding the
+	`merge.conflictStyle` configuration variable.  Possible values
+	are "merge" (default) and "diff3" (in addition to what is
+	shown by "merge" style, shows the original contents).
+
+--ignore-unmerged::
+	When restoring files on the working tree from the index, do
+	not abort the operation if there are unmerged entries and
+	neither `--ours`, `--theirs`, `--merge` or `--conflict` is
+	specified. Unmerged paths on the working tree are left alone.
+
+--ignore-skip-worktree-bits::
+	In sparse checkout mode, by default is to only update entries
+	matched by `<pathspec>` and sparse patterns in
+	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
+	patterns and unconditionally restores any files in
+	`<pathspec>`.
+
+--overlay::
+--no-overlay::
+	In overlay mode, the command never removes files when
+	restoring. In no-overlay mode, tracked files that do not
+	appear in the `--source` tree are removed, to make them match
+	`<tree>` exactly. The default is no-overlay mode.
+
+EXAMPLES
+--------
+
+The following sequence switches to the `master` branch, reverts the
+`Makefile` to two revisions back, deletes hello.c by mistake, and gets
+it back from the index.
+
+------------
+$ git switch master
+$ git restore --source master~2 Makefile  <1>
+$ rm -f hello.c
+$ git restore hello.c                     <2>
+------------
+
+<1> take a file out of another commit
+<2> restore hello.c from the index
+
+If you want to restore _all_ C source files to match the version in
+the index, you can say
+
+------------
+$ git restore '*.c'
+------------
+
+Note the quotes around `*.c`.  The file `hello.c` will also be
+restored, even though it is no longer in the working tree, because the
+file globbing is used to match entries in the index (not in the
+working tree by the shell).
+
+To restore all files in the current directory
+
+------------
+$ git restore .
+------------
+
+or to restore all working tree files with 'top' pathspec magic (see
+linkgit:gitglossary[7])
+
+------------
+$ git restore :/
+------------
+
+To restore a file in the index to match the version in `HEAD` (this is
+the same as using linkgit:git-reset[1])
+
+------------
+$ git restore --staged hello.c
+------------
+
+or you can restore both the index and the working tree (this the same
+as using linkgit:git-checkout[1])
+
+------------
+$ git restore --source=HEAD --staged --worktree hello.c
+------------
+
+or the short form which is more practical but less readable:
+
+------------
+$ git restore -s@ -SW hello.c
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-reset[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 837707a8fd..018ecf49d3 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -30,6 +30,9 @@ should see linkgit:git-checkout[1], specifically the `git checkout
 <commit> -- <filename>` syntax.  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
 OPTIONS
 -------
 <commit>...::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 00156d64aa..fbed007354 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -210,6 +210,26 @@ people via patch over e-mail.
 
 include::cmds-foreignscminterface.txt[]
 
+Reset, restore and revert
+~~~~~~~~~~~~~~~~~~~~~~~~~
+There are three commands with similar names: `git reset`,
+`git restore` and `git revert`.
+
+* linkgit:git-revert[1] is about making a new commit that reverts the
+  changes made by other commits.
+
+* linkgit:git-restore[1] is about restoring files in the working tree
+  from either the index or another commit. This command does not
+  update your branch. The command can also be used to restore files in
+  the index from another commit.
+
+* linkgit:git-reset[1] is about updating your branch, moving the tip
+  in order to add or remove commits from the branch. This operation
+  changes the commit history.
++
+`git reset` can also be used to restore the index, overlapping with
+`git restore`.
+
 
 Low-level commands (plumbing)
 -----------------------------
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 592e06d839..225513ef5e 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -209,6 +209,18 @@ See also http://marc.info/?l=git&m=116563135620359 and
 http://marc.info/?l=git&m=119150393620273 for further
 information.
 
+Some other commands that also work on files in the working tree and/or
+in the index can take `--staged` and/or `--worktree`.
+
+* `--staged` is exactly like `--cached`, which is used to ask a
+  command to only work on the index, not the working tree.
+
+* `--worktree` is the opposite, to ask a command to work on the
+  working tree only, not the index.
+
+* The two options can be specified together to ask a command to work
+  on both the index and the working tree.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 8e91db73ad..ffe7e4f58f 100644
--- a/Makefile
+++ b/Makefile
@@ -799,6 +799,7 @@ BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
+BUILT_INS += git-restore$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
diff --git a/builtin.h b/builtin.h
index c64e44450e..6830000e14 100644
--- a/builtin.h
+++ b/builtin.h
@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_repack(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
+extern int cmd_restore(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0351735c6e..98dc2ded38 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -38,6 +38,11 @@ static const char * const switch_branch_usage[] = {
 	NULL,
 };
 
+static const char * const restore_usage[] = {
+	N_("git restore [<options>] [<branch>] -- <file>..."),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1622,3 +1627,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	FREE_AND_NULL(options);
 	return ret;
 }
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_pathspec = 1;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, restore_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 13317f47d4..b9eae1c258 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           worktree
+git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
 git-rev-parse                           plumbinginterrogators
diff --git a/git.c b/git.c
index 39582cf511..6d439e723f 100644
--- a/git.c
+++ b/git.c
@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "replace", cmd_replace, RUN_SETUP },
 	{ "rerere", cmd_rerere, RUN_SETUP },
 	{ "reset", cmd_reset, RUN_SETUP },
+	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
 	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
 	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
 	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 02/16] restore: take tree-ish from --source option instead
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
                       ` (14 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

This is another departure from 'git checkout' syntax, which uses -- to
separate ref and pathspec. The observation is restore (or "git
checkout -- <pathspec>") is most often used to restore some files from
the index. If this is correct, we can simplify it by taking away the
ref, so that we can write

    git restore some-file

without worrying about some-file being a ref and whether we need to do

    git restore -- some-file

for safety. If the source of the restore comes from a tree, it will be
in the form of an option with value, e.g.

    git restore --source=this-tree some-file

This is of course longer to type than using "--". But hopefully it
will not be used as often, and it is clearly easier to understand.

dwim_new_local_branch is no longer set (or unset) in cmd_restore_files()
because it's irrelevant because we don't really care about dwim-ing.
With accept_ref being unset, dwim can't happen.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 98dc2ded38..5aba345712 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -39,7 +39,7 @@ static const char * const switch_branch_usage[] = {
 };
 
 static const char * const restore_usage[] = {
-	N_("git restore [<options>] [<branch>] -- <file>..."),
+	N_("git restore [<options>] [--source=<branch>] <file>..."),
 	NULL,
 };
 
@@ -59,6 +59,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int dwim_new_local_branch;
 	int discard_changes;
+	int accept_ref;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
@@ -76,6 +77,7 @@ struct checkout_opts {
 	int branch_exists;
 	const char *prefix;
 	struct pathspec pathspec;
+	const char *from_treeish;
 	struct tree *source_tree;
 };
 
@@ -1410,6 +1412,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
+	int parseopt_flags = 0;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1421,8 +1424,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	argc = parse_options(argc, argv, prefix, options, usagestr,
-			     PARSE_OPT_KEEP_DASHDASH);
+	if (!opts->accept_pathspec && !opts->accept_ref)
+		BUG("make up your mind, you need to take _something_");
+	if (opts->accept_pathspec && opts->accept_ref)
+		parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     usagestr, parseopt_flags);
 
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
@@ -1481,7 +1489,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	 * including "last branch" syntax and DWIM-ery for names of
 	 * remote branches, erroring out for invalid or ambiguous cases.
 	 */
-	if (argc) {
+	if (argc && opts->accept_ref) {
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
@@ -1493,6 +1501,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
+	} else if (!opts->accept_ref && opts->from_treeish) {
+		struct object_id rev;
+
+		if (get_oid_mb(opts->from_treeish, &rev))
+			die(_("could not resolve %s"), opts->from_treeish);
+
+		setup_new_branch_info_and_source_tree(&new_branch_info,
+						      opts, &rev,
+						      opts->from_treeish);
+
+		if (!opts->source_tree)
+			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
 	if (argc) {
@@ -1576,6 +1596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.only_merge_on_switching_branches = 0;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
@@ -1611,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
+	opts.accept_ref = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
@@ -1631,15 +1653,19 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 int cmd_restore(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option restore_options[] = {
+		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+			   N_("where the checkout from")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.dwim_new_local_branch = 1;
-	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 03/16] restore: make pathspec mandatory
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
                       ` (13 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

"git restore" without arguments does not make much sense when
it's about restoring files (what files now?). We could default to
either

    git restore .

or

    git restore :/

Neither is intuitive. Make the user always give pathspec, force the
user to think the scope of restore they want because this is a
destructive operation.

"git restore -p" without pathspec is an exception to this
because it really is a separate mode. It will be treated as running
patch mode on the whole worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5aba345712..77db5236f0 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -65,6 +65,7 @@ struct checkout_opts {
 	int only_merge_on_switching_branches;
 	int can_switch_when_in_progress;
 	int orphan_from_empty_tree;
+	int empty_pathspec_ok;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1515,6 +1516,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
+	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+	    !opts->patch_mode)	/* patch mode is special */
+		die(_("you must specify path(s) to restore"));
+
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
 			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1601,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
 	opts.orphan_from_empty_tree = 0;
+	opts.empty_pathspec_ok = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1664,6 +1670,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
+	opts.empty_pathspec_ok = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 04/16] restore: disable overlay mode by default
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (2 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
                       ` (12 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Overlay mode is considered confusing when the command is about
restoring files on worktree. Disable it by default. The user can still
turn it on, or use 'git checkout' which still has overlay mode on by
default.

While at it, make the check in checkout_branch() stricter. Neither
--overlay or --no-overlay should be accepted in branch switching mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 77db5236f0..9c5abe170e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1274,9 +1274,9 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
 
-	if (!opts->overlay_mode)
+	if (opts->overlay_mode != -1)
 		die(_("'%s' cannot be used with switching branches"),
-		    "--no-overlay");
+		    "--[no]-overlay");
 
 	if (opts->writeout_stage)
 		die(_("'%s' cannot be used with switching branches"),
@@ -1399,7 +1399,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	struct option *newopts = parse_options_concat(prevopts, options);
@@ -1419,7 +1418,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
-	opts->overlay_mode = -1;
 
 	git_config(git_checkout_config, opts);
 
@@ -1593,6 +1591,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
 			 N_("second guess 'git checkout <no-such-branch>' (default)")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
 	int ret;
@@ -1607,6 +1606,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.can_switch_when_in_progress = 1;
 	opts.orphan_from_empty_tree = 0;
 	opts.empty_pathspec_ok = 1;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1645,6 +1645,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.implicit_detach = 0;
 	opts.can_switch_when_in_progress = 0;
 	opts.orphan_from_empty_tree = 1;
+	opts.overlay_mode = -1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
@@ -1663,6 +1664,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
 	int ret;
@@ -1671,6 +1673,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
+	opts.overlay_mode = 0;
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 05/16] checkout: factor out worktree checkout code
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (3 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
                       ` (11 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 108 +++++++++++++++++++++++++--------------------
 1 file changed, 59 insertions(+), 49 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9c5abe170e..1753c8c5ed 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -330,17 +330,73 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 	}
 }
 
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+	struct checkout state = CHECKOUT_INIT;
+	int nr_checkouts = 0, nr_unmerged = 0;
+	int errs = 0;
+	int pos;
+
+	state.force = 1;
+	state.refresh_cache = 1;
+	state.istate = &the_index;
+
+	enable_delayed_checkout(&state);
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce->ce_flags & CE_MATCHED) {
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state,
+						       NULL, &nr_checkouts);
+				continue;
+			}
+			if (opts->writeout_stage)
+				errs |= checkout_stage(opts->writeout_stage,
+						       ce, pos,
+						       &state,
+						       &nr_checkouts, opts->overlay_mode);
+			else if (opts->merge)
+				errs |= checkout_merged(pos, &state,
+							&nr_unmerged);
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	remove_marked_cache_entries(&the_index, 1);
+	remove_scheduled_dirs();
+	errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+	if (opts->count_checkout_paths) {
+		if (nr_unmerged)
+			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+					      "Recreated %d merge conflicts",
+					      nr_unmerged),
+				   nr_unmerged);
+		if (opts->source_tree)
+			fprintf_ln(stderr, Q_("Updated %d path from %s",
+					      "Updated %d paths from %s",
+					      nr_checkouts),
+				   nr_checkouts,
+				   find_unique_abbrev(&opts->source_tree->object.oid,
+						      DEFAULT_ABBREV));
+		else if (!nr_unmerged || nr_checkouts)
+			fprintf_ln(stderr, Q_("Updated %d path from the index",
+					      "Updated %d paths from the index",
+					      nr_checkouts),
+				   nr_checkouts);
+	}
+
+	return errs;
+}
+
 static int checkout_paths(const struct checkout_opts *opts,
 			  const char *revision)
 {
 	int pos;
-	struct checkout state = CHECKOUT_INIT;
 	static char *ps_matched;
 	struct object_id rev;
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
-	int nr_checkouts = 0, nr_unmerged = 0;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -426,53 +482,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	state.force = 1;
-	state.refresh_cache = 1;
-	state.istate = &the_index;
-
-	enable_delayed_checkout(&state);
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		if (ce->ce_flags & CE_MATCHED) {
-			if (!ce_stage(ce)) {
-				errs |= checkout_entry(ce, &state,
-						       NULL, &nr_checkouts);
-				continue;
-			}
-			if (opts->writeout_stage)
-				errs |= checkout_stage(opts->writeout_stage,
-						       ce, pos,
-						       &state,
-						       &nr_checkouts, opts->overlay_mode);
-			else if (opts->merge)
-				errs |= checkout_merged(pos, &state,
-							&nr_unmerged);
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-	remove_marked_cache_entries(&the_index, 1);
-	remove_scheduled_dirs();
-	errs |= finish_delayed_checkout(&state, &nr_checkouts);
-
-	if (opts->count_checkout_paths) {
-		if (nr_unmerged)
-			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
-					      "Recreated %d merge conflicts",
-					      nr_unmerged),
-				   nr_unmerged);
-		if (opts->source_tree)
-			fprintf_ln(stderr, Q_("Updated %d path from %s",
-					      "Updated %d paths from %s",
-					      nr_checkouts),
-				   nr_checkouts,
-				   find_unique_abbrev(&opts->source_tree->object.oid,
-						      DEFAULT_ABBREV));
-		else if (!nr_unmerged || nr_checkouts)
-			fprintf_ln(stderr, Q_("Updated %d path from the index",
-					      "Updated %d paths from the index",
-					      nr_checkouts),
-				   nr_checkouts);
-	}
+	errs |= checkout_worktree(opts);
 
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 06/16] restore: add --worktree and --staged
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (4 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
                       ` (10 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

'git checkout <tree-ish> <pathspec>' updates both index and
worktree. But updating the index when you want to restore worktree
files is non-intuitive. The index contains the data ready for the next
commit, and there's no indication that the user will want to commit
the restored versions.

'git restore' therefore by default only touches worktree. The user has
the option to update either the index with

    git restore --staged --source=<tree> <path>  (1)

or update both with

    git restore --staged --worktree --source=<tree> <path> (2)

PS. Orignally I wanted to make worktree update default and form (1)
would add index update while also updating the worktree, and the user
would need to do "--staged --no-worktree" to update index only. But it
looks really confusing that "--staged" option alone updates both. So
now form (2) is used for both, which reads much more obvious.

PPS. Yes form (1) overlaps with "git reset <rev> <path>". I don't know
if we can ever turn "git reset" back to "_always_ reset HEAD and
optionally do something else".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 6 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1753c8c5ed..e855c64cfe 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -66,6 +66,8 @@ struct checkout_opts {
 	int can_switch_when_in_progress;
 	int orphan_from_empty_tree;
 	int empty_pathspec_ok;
+	int checkout_index;
+	int checkout_worktree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -397,6 +399,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	struct commit *head;
 	int errs = 0;
 	struct lock_file lock_file = LOCK_INIT;
+	int checkout_index;
 
 	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -422,9 +425,26 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
 		    opts->new_branch);
 
-	if (opts->patch_mode)
-		return run_add_interactive(revision, "--patch=checkout",
-					   &opts->pathspec);
+	if (!opts->checkout_worktree && !opts->checkout_index)
+		die(_("neither '%s' or '%s' is specified"),
+		    "--staged", "--worktree");
+
+	if (!opts->checkout_worktree && !opts->from_treeish)
+		die(_("'%s' must be used when '%s' is not specified"),
+		    "--worktree", "--source");
+
+	if (opts->patch_mode) {
+		const char *patch_mode;
+
+		if (opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=checkout";
+		else if (opts->checkout_index && !opts->checkout_worktree)
+			patch_mode = "--patch=reset";
+		else
+			die(_("'%s' with only '%s' is not currently supported"),
+			    "--patch", "--worktree");
+		return run_add_interactive(revision, patch_mode, &opts->pathspec);
+	}
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(&opts->pathspec) < 0)
@@ -482,10 +502,30 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return 1;
 
 	/* Now we are committed to check them out */
-	errs |= checkout_worktree(opts);
+	if (opts->checkout_worktree)
+		errs |= checkout_worktree(opts);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-		die(_("unable to write new index file"));
+	/*
+	 * Allow updating the index when checking out from the index.
+	 * This is to save new stat info.
+	 */
+	if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+		checkout_index = 1;
+	else
+		checkout_index = opts->checkout_index;
+
+	if (checkout_index) {
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die(_("unable to write new index file"));
+	} else {
+		/*
+		 * NEEDSWORK: if --worktree is not specified, we
+		 * should save stat info of checked out files in the
+		 * index to avoid the next (potentially costly)
+		 * refresh. But it's a bit tricker to do...
+		 */
+		rollback_lock_file(&lock_file);
+	}
 
 	read_ref_full("HEAD", 0, &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -1461,6 +1501,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
+	if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = 0;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = 0;
+	} else {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = -opts->checkout_index - 1;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = -opts->checkout_worktree - 1;
+	}
+	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+		BUG("these flags should be non-negative by now");
+
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
 	 * and new_branch_force and new_orphan_branch will tell us which one of
@@ -1617,6 +1671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.orphan_from_empty_tree = 0;
 	opts.empty_pathspec_ok = 1;
 	opts.overlay_mode = -1;
+	opts.checkout_index = -2;    /* default on */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1674,6 +1730,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
 			   N_("where the checkout from")),
+		OPT_BOOL('S', "staged", &opts.checkout_index,
+			   N_("restore the index")),
+		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+			   N_("restore the working tree (default)")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1684,6 +1744,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
 	opts.overlay_mode = 0;
+	opts.checkout_index = -1;    /* default off */
+	opts.checkout_worktree = -2; /* default on */
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 07/16] restore: reject invalid combinations with --staged
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (5 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
                       ` (9 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

git-checkout rejects plenty of invalid option combinations. Since
git-checkout is equivalent of either

    git restore --source --staged --worktree

or

    git restore --worktree

that still leaves the new mode 'git restore --index' unprotected. Reject
some more invalid option combinations.

The other new mode 'restore --source --worktree' does not need anything
else.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index e855c64cfe..71e2589340 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -433,6 +433,16 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("'%s' must be used when '%s' is not specified"),
 		    "--worktree", "--source");
 
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->writeout_stage)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--ours", "--theirs", "--staged");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->merge)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--merge", "--conflict", "--staged");
+
 	if (opts->patch_mode) {
 		const char *patch_mode;
 
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 08/16] restore: default to --source=HEAD when only --staged is specified
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (6 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
                       ` (8 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

"git restore --staged" without --source does not make much sense since
by default we restore from the index.  Instead of copying the index to
itself, set the default source to HEAD in this case, yielding behavior
that matches "git reset -- <paths>".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e2589340..09a03f1ff8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1524,6 +1524,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	}
 	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
 		BUG("these flags should be non-negative by now");
+	/*
+	 * convenient shortcut: "git restore --staged" equals
+	 * "git restore --staged --source HEAD"
+	 */
+	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+		opts->from_treeish = "HEAD";
 
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 09/16] restore: replace --force with --ignore-unmerged
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (7 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
                       ` (7 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Use a more specific option name to express its purpose. --force may come
back as an alias of --ignore-unmerged and possibly more. But since this
is a destructive operation, I don't see why we need to "force" anything
more. We already don't hold back.

When 'checkout --force' or 'restore --ignore-unmerged' is used, we may
also print warnings about unmerged entries being ignore. Since this is
not exactly warning (people tell us to do so), more informational, let
it be suppressed if --quiet is given. This is a behavior change for
git-checkout.

PS. The diff looks a bit iffy since --force is moved to
add_common_switch_branch_options() (i.e. for switching). But
git-checkout is also doing switching and inherits this --force.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 09a03f1ff8..824ab65886 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -68,6 +68,8 @@ struct checkout_opts {
 	int empty_pathspec_ok;
 	int checkout_index;
 	int checkout_worktree;
+	const char *ignore_unmerged_opt;
+	int ignore_unmerged;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -409,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->new_branch_log)
 		die(_("'%s' cannot be used with updating paths"), "-l");
 
-	if (opts->force && opts->patch_mode)
-		die(_("'%s' cannot be used with updating paths"), "-f");
+	if (opts->ignore_unmerged && opts->patch_mode)
+		die(_("'%s' cannot be used with updating paths"),
+		    opts->ignore_unmerged_opt);
 
 	if (opts->force_detach)
 		die(_("'%s' cannot be used with updating paths"), "--detach");
@@ -418,8 +421,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->merge && opts->patch_mode)
 		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
 
-	if (opts->force && opts->merge)
-		die(_("'%s' cannot be used with %s"), "-f", "-m");
+	if (opts->ignore_unmerged && opts->merge)
+		die(_("'%s' cannot be used with %s"),
+		    opts->ignore_unmerged_opt, "-m");
 
 	if (opts->new_branch)
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
@@ -495,8 +499,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
-			if (opts->force) {
-				warning(_("path '%s' is unmerged"), ce->name);
+			if (opts->ignore_unmerged) {
+				if (!opts->quiet)
+					warning(_("path '%s' is unmerged"), ce->name);
 			} else if (opts->writeout_stage) {
 				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
 			} else if (opts->merge) {
@@ -1414,8 +1419,6 @@ static struct option *add_common_options(struct checkout_opts *opts,
 			    "checkout", "control recursive updating of submodules",
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
 		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
 		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
 		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
@@ -1433,6 +1436,8 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
@@ -1502,8 +1507,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
-	if (opts->force)
+	if (opts->force) {
 		opts->discard_changes = 1;
+		opts->ignore_unmerged_opt = "--force";
+		opts->ignore_unmerged = 1;
+	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1750,6 +1758,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 			   N_("restore the index")),
 		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
 			   N_("restore the working tree (default)")),
+		OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+			 N_("ignore unmerged entries")),
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
@@ -1762,6 +1772,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
 	opts.overlay_mode = 0;
 	opts.checkout_index = -1;    /* default off */
 	opts.checkout_worktree = -2; /* default on */
+	opts.ignore_unmerged_opt = "--ignore-unmerged";
 
 	options = parse_options_dup(restore_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 10/16] restore: support --patch
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (8 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
                       ` (6 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

git-restore is different from git-checkout that it only restores the
worktree by default, not both worktree and index. add--interactive
needs some update to support this mode.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c        |  6 +++--
 git-add--interactive.perl | 52 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 824ab65886..bed79ae595 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -454,9 +454,11 @@ static int checkout_paths(const struct checkout_opts *opts,
 			patch_mode = "--patch=checkout";
 		else if (opts->checkout_index && !opts->checkout_worktree)
 			patch_mode = "--patch=reset";
+		else if (!opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=worktree";
 		else
-			die(_("'%s' with only '%s' is not currently supported"),
-			    "--patch", "--worktree");
+			BUG("either flag must have been set, worktree=%d, index=%d",
+			    opts->checkout_worktree, opts->checkout_index);
 		return run_add_interactive(revision, patch_mode, &opts->pathspec);
 	}
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 20eb81cc92..3dfb3629c9 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -149,6 +149,20 @@ sub colored {
 		FILTER => undef,
 		IS_REVERSE => 0,
 	},
+	'worktree_head' => {
+		DIFF => 'diff-index -p',
+		APPLY => sub { apply_patch 'apply -R', @_ },
+		APPLY_CHECK => 'apply -R',
+		FILTER => undef,
+		IS_REVERSE => 1,
+	},
+	'worktree_nothead' => {
+		DIFF => 'diff-index -R -p',
+		APPLY => sub { apply_patch 'apply', @_ },
+		APPLY_CHECK => 'apply',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
 );
 
 $patch_mode = 'stage';
@@ -1049,6 +1063,12 @@ sub color_diff {
 marked for discarding."),
 	checkout_nothead => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+	worktree_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+	worktree_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
 marked for applying."),
 );
 
@@ -1259,6 +1279,18 @@ sub edit_hunk_loop {
 n - do not apply this hunk to index and worktree
 q - quit; do not apply this hunk or any of the remaining ones
 a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+	worktree_head => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+	worktree_nothead => N__(
+"y - apply this hunk to worktree
+n - do not apply this hunk to worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
 d - do not apply this hunk or any of the later hunks in the file"),
 );
 
@@ -1421,6 +1453,16 @@ sub display_hunks {
 		deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
 		hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
 	},
+	worktree_head => {
+		mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+	},
+	worktree_nothead => {
+		mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+	},
 );
 
 sub patch_update_file {
@@ -1756,6 +1798,16 @@ sub process_args {
 						       'checkout_head' : 'checkout_nothead');
 					$arg = shift @ARGV or die __("missing --");
 				}
+			} elsif ($1 eq 'worktree') {
+				$arg = shift @ARGV or die __("missing --");
+				if ($arg eq '--') {
+					$patch_mode = 'checkout_index';
+				} else {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'worktree_head' : 'worktree_nothead');
+					$arg = shift @ARGV or die __("missing --");
+				}
 			} elsif ($1 eq 'stage' or $1 eq 'stash') {
 				$patch_mode = $1;
 				$arg = shift @ARGV or die __("missing --");
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 11/16] t: add tests for restore
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (9 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-06-13 15:19       ` SZEDER Gábor
  2019-04-25  9:45     ` [PATCH v3 12/16] completion: support restore Nguyễn Thái Ngọc Duy
                       ` (5 subsequent siblings)
  16 siblings, 1 reply; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/lib-patch-mode.sh               |  12 ++++
 t/t2070-restore.sh (new +x)       |  99 +++++++++++++++++++++++++++
 t/t2071-restore-patch.sh (new +x) | 110 ++++++++++++++++++++++++++++++
 3 files changed, 221 insertions(+)

diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 06c3c91762..cfd76bf987 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,28 +2,40 @@
 
 . ./test-lib.sh
 
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
 set_state () {
 	echo "$3" > "$1" &&
 	git add "$1" &&
 	echo "$2" > "$1"
 }
 
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
 save_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	cat "$1" > _worktree_"$noslash" &&
 	git show :"$1" > _index_"$noslash"
 }
 
+# set_and_save_state <path> <worktree-content> <index-content>
 set_and_save_state () {
 	set_state "$@" &&
 	save_state "$1"
 }
 
+# verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
 	test "$(cat "$1")" = "$2" &&
 	test "$(git show :"$1")" = "$3"
 }
 
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
 verify_saved_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
new file mode 100755
index 0000000000..73ea13ede9
--- /dev/null
+++ b/t/t2070-restore.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='restore basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	echo first-and-a-half >>first.t &&
+	git add first.t &&
+	test_commit second &&
+	echo one >one &&
+	echo two >two &&
+	echo untracked >untracked &&
+	echo ignored >ignored &&
+	echo /ignored >.gitignore &&
+	git add one two .gitignore &&
+	git update-ref refs/heads/one master
+'
+
+test_expect_success 'restore without pathspec is not ok' '
+	test_must_fail git restore &&
+	test_must_fail git restore --source=first
+'
+
+test_expect_success 'restore a file, ignoring branch of same name' '
+	cat one >expected &&
+	echo dirty >>one &&
+	git restore one &&
+	test_cmp expected one
+'
+
+test_expect_success 'restore a file on worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first first.t &&
+	test_cmp expected first.t &&
+	git cat-file blob HEAD:./first.t >expected &&
+	git show :first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore a file in the index from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	git cat-file blob HEAD:./first.t >expected &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore a file in both the index and worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged --worktree first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore --staged uses HEAD as source' '
+	test_when_finished git reset --hard &&
+	git cat-file blob :./first.t >expected &&
+	echo index-dirty >>first.t &&
+	git add first.t &&
+	git restore --staged first.t &&
+	git cat-file blob :./first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
+	git init unmerged &&
+	(
+		cd unmerged &&
+		echo one >unmerged &&
+		echo one >common &&
+		git add unmerged common &&
+		git commit -m common &&
+		git switch -c first &&
+		echo first >unmerged &&
+		git commit -am first &&
+		git switch -c second master &&
+		echo second >unmerged &&
+		git commit -am second &&
+		test_must_fail git merge first &&
+
+		echo dirty >>common &&
+		test_must_fail git restore . &&
+
+		git restore --ignore-unmerged --quiet . >output 2>&1 &&
+		git diff common >diff-output &&
+		: >empty &&
+		test_cmp empty output &&
+		test_cmp empty diff-output
+	)
+'
+
+test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
new file mode 100755
index 0000000000..98b2476e7c
--- /dev/null
+++ b/t/t2071-restore-patch.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='git restore --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent >dir/foo &&
+	echo dummy >bar &&
+	git add bar dir/foo &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+test_expect_success PERL 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n n | git restore -p &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git restore -p' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git restore -p with staged changes' '
+	set_state dir/foo work index &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD^' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD^ &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent index
+'
+
+test_expect_success PERL 'git restore -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent head
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | (cd dir && git restore -p foo) &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 12/16] completion: support restore
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (10 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
                       ` (4 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Completion for restore is straightforward. We could still do better
though by giving the list of just tracked files instead of all present
ones. But let's leave it for later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b24bc48276..58d18d41a2 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2491,6 +2491,21 @@ _git_reset ()
 	__git_complete_refs
 }
 
+_git_restore ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--source=*)
+		__git_complete_refs --cur="${cur##--source=}"
+		;;
+	--*)
+		__gitcomp_builtin restore
+		;;
+	esac
+}
+
 __git_revert_inprogress_options="--continue --quit --abort"
 
 _git_revert ()
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard'
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (11 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 12/16] completion: support restore Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
                       ` (3 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Since the operation in progress is merge, stick to the 'git merge'
variant of aborting. 'git reset --hard' does not really tell you about
aborting the merge by just looking, longer to type, and even though I
know by heart what --hard do, I still dislike it when I need to consider
whether --hard, --mixed or --soft.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/user-manual.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 94799faa2b..4e210970e1 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess
 away, you can always return to the pre-merge state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git merge --abort
 -------------------------------------------------
 
 Or, if you've already committed the merge that you want to throw away,
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 14/16] doc: promote "git restore"
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (12 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:45     ` [PATCH v3 15/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
                       ` (2 subsequent siblings)
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

The new command "git restore" (together with "git switch") are added
to avoid the confusion of one-command-do-all "git checkout" for new
users. They are also helpful to avoid ambiguous context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands.

One nice thing about git-restore is the ability to restore
"everything", so it can be used in "git status" advice instead of both
"git checkout" and "git reset".  The three commands suggested by "git
status" are add, rm and restore.

"git checkout" is also removed from "git help" (i.e. it's no longer
considered a commonly used command)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clean.txt        |  2 +-
 Documentation/git-commit.txt       |  2 +-
 Documentation/git-format-patch.txt |  2 +-
 Documentation/git-reset.txt        |  6 +--
 Documentation/git-revert.txt       |  4 +-
 Documentation/gitcli.txt           |  4 +-
 Documentation/giteveryday.txt      |  5 +-
 Documentation/gittutorial-2.txt    |  4 +-
 Documentation/gittutorial.txt      |  2 +-
 Documentation/user-manual.txt      | 12 ++---
 builtin/clone.c                    |  2 +-
 builtin/commit.c                   |  2 +-
 command-list.txt                   |  2 +-
 t/t7508-status.sh                  | 82 +++++++++++++++---------------
 t/t7512-status-help.sh             | 20 ++++----
 wt-status.c                        | 26 +++++++---
 16 files changed, 93 insertions(+), 84 deletions(-)

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 03056dad0d..8a31ccffea 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -64,7 +64,7 @@ OPTIONS
 	directory) and $GIT_DIR/info/exclude, but do still use the ignore
 	rules given with `-e` options.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
-	conjunction with 'git reset') to create a pristine
+	conjunction with 'git restore' or 'git reset') to create a pristine
 	working directory to test a clean build.
 
 -X::
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a85c2c2a4c..7628193284 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD -- <file>`,
+to that of the last commit with `git restore --staged <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 0a24a5679e..01bfcecf69 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -422,7 +422,7 @@ One way to test if your MUA is set up correctly is:
 
     $ git fetch <project> master:test-apply
     $ git switch test-apply
-    $ git reset --hard
+    $ git restore --source=HEAD --staged --worktree :/
     $ git am a.patch
 
 If it does not apply correctly, there can be various reasons.
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index c25f8a95b9..633d71d36a 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -29,9 +29,9 @@ This means that `git reset <paths>` is the opposite of `git add
 `git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
-use linkgit:git-checkout[1] to check the contents out of the index to
-the working tree.
-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+use linkgit:git-restore[1] to check the contents out of the index to
+the working tree. Alternatively, using linkgit:git-restore[1]
+and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 018ecf49d3..9aadc36881 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -26,8 +26,8 @@ effect of some earlier commits (often only a faulty one).  If you want to
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the `--hard` option.  If
 you want to extract specific files as they were in another commit, you
-should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> -- <filename>` syntax.  Take care with these alternatives as
+should see linkgit:git-restore[1], specifically the `--source`
+option. Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 See "Reset, restore and revert" in linkgit:git[1] for the differences
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 225513ef5e..1ed3ca33b7 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.
    things:
 +
 --------------------------------
-$ git checkout -- *.c
-$ git checkout -- \*.c
+$ git restore *.c
+$ git restore \*.c
 --------------------------------
 +
 The former lets your shell expand the fileglob, and you are asking
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index ad455f3e39..1bd919f92b 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -51,8 +51,7 @@ following commands.
 
   * linkgit:git-commit[1] to advance the current branch.
 
-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with
-    pathname parameters) to undo changes.
+  * linkgit:git-restore[1] to undo changes.
 
   * linkgit:git-merge[1] to merge between local branches.
 
@@ -82,7 +81,7 @@ Create a topic branch and develop.::
 ------------
 $ git switch -c alsa-audio <1>
 $ edit/compile/test
-$ git checkout -- curses/ux_audio_oss.c <2>
+$ git restore curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
 $ edit/compile/test
 $ git diff HEAD <4>
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index e0976f6017..8bdb7d0bd3 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -370,13 +370,13 @@ situation:
 $ git status
 On branch master
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   closing.txt
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   file.txt
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index e6ad6b5f8d..59ef5cef1f 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -110,7 +110,7 @@ $ git status
 On branch master
 Changes to be committed:
 Your branch is up to date with 'origin/master'.
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   file1
 	modified:   file2
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 4e210970e1..8bce75b2cf 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed
 state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git restore --staged --worktree :/
 -------------------------------------------------
 
 If you make a commit that you later wish you hadn't, there are two
@@ -1523,12 +1523,10 @@ Checking out an old version of a file
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used `git checkout` before to switch
-branches, but it has quite different behavior if it is given a path
-name: the command
+linkgit:git-restore[1]. The command
 
 -------------------------------------------------
-$ git checkout HEAD^ path/to/file
+$ git restore --source=HEAD^ path/to/file
 -------------------------------------------------
 
 replaces path/to/file by the contents it had in the commit HEAD^, and
@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.
 The Workflow
 ------------
 
-High-level operations such as linkgit:git-commit[1],
-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data
+High-level operations such as linkgit:git-commit[1] and
+linkgit:git-restore[1] work by moving data
 between the working tree, the index, and the object database.  Git
 provides low-level operations which perform each of these steps
 individually.
diff --git a/builtin/clone.c b/builtin/clone.c
index 50bde99618..024eb57996 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -492,7 +492,7 @@ static enum {
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry with 'git restore --source=HEAD :/'\n");
 
 static void remove_junk(void)
 {
diff --git a/builtin/commit.c b/builtin/commit.c
index f17537474a..fa5982cc86 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1676,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
-		      "not exceeded, and then \"git reset HEAD\" to recover."));
+		      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
 	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 		write_commit_graph_reachable(get_object_directory(), 0, 0);
diff --git a/command-list.txt b/command-list.txt
index b9eae1c258..cf8dccb439 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
 git-check-mailmap                       purehelpers
-git-checkout                            mainporcelain           history
+git-checkout                            mainporcelain
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
 git-cherry                              plumbinginterrogators          complete
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e1f11293e2..681bc314b4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -94,13 +94,13 @@ test_expect_success 'status --column' '
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -128,13 +128,13 @@ cat >expect <<\EOF
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   ../dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   modified
 
@@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	<GREEN>new file:   dir2/added<RESET>
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	<RED>modified:   dir1/modified<RESET>
 
@@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   dir1/modified
 
@@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD^1 <file>..." to unstage)
+  (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
   (commit or discard the untracked or modified content in submodules)
 
 	modified:   dir1/modified
@@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 	modified:   sm (new commits)
@@ -1379,13 +1379,13 @@ cat > expect << EOF
 ;   (use "git pull" to merge the remote branch into yours)
 ;
 ; Changes to be committed:
-;   (use "git reset HEAD <file>..." to unstage)
+;   (use "git restore --staged <file>..." to unstage)
 ;
 ;	modified:   sm
 ;
 ; Changes not staged for commit:
 ;   (use "git add <file>..." to update what will be committed)
-;   (use "git checkout -- <file>..." to discard changes in working directory)
+;   (use "git restore <file>..." to discard changes in working directory)
 ;
 ;	modified:   dir1/modified
 ;	modified:   sm (new commits)
@@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 458608cc1e..1b9712c675 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -85,7 +85,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -110,7 +110,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -148,7 +148,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -176,7 +176,7 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -246,7 +246,7 @@ You are currently splitting a commit while rebasing branch '\''split_commit'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -354,7 +354,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -453,7 +453,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -557,7 +557,7 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -816,7 +816,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   to-revert.txt
@@ -837,7 +837,7 @@ You are currently reverting commit $TO_REVERT.
   (use "git revert --abort" to cancel the revert operation)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   to-revert.txt
 
diff --git a/wt-status.c b/wt-status.c
index 445a36204a..19fd1add75 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -178,9 +178,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		;
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 
 	if (!both_deleted) {
@@ -205,9 +211,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
 		return;
 	if (s->whence != FROM_COMMIT)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c
+					 , _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
@@ -225,7 +237,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,
 		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
 	else
 		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
-	status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+	status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
 		status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
 	status_printf_ln(s, c, "%s", "");
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 15/16] help: move git-diff and git-reset to different groups
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (13 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:45     ` Nguyễn Thái Ngọc Duy
  2019-04-25  9:46     ` [PATCH v3 16/16] Declare both git-switch and git-restore experimental Nguyễn Thái Ngọc Duy
  2019-05-07  2:21     ` [PATCH v3 00/16] Add new command 'restore' Emily Shaffer
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:45 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

The third column in command-list.txt determines what group a common
command is printed in 'git help'.

"git reset" is currently in the "work on the current change (see also:
git help everyday)" group. While it's true that "git reset" can
manipulate the index and can be in this group, its unique
functionality is resetting HEAD, which should be the "grow, mark,
tweak history" group.

Moving it there will also avoid the confusion because both 'restore'
and 'reset' are in the same group, next to each other.

While looking at the 'group, mark, tweak history', I realize "git
diff" should not be there. All the commands in this group is about
_changing_ the commit history while "git diff" is a read-only
operation. It fits better in the "examine the history and state" group
(especially when "git status", its close friend, is already there).

This is what we have after the reorganization:

    work on the current change (see also: git help everyday)
       add       Add file contents to the index
       mv        Move or rename a file, a directory, or a symlink
       restore   Restore working tree files
       rm        Remove files from the working tree and from the index

    examine the history and state (see also: git help revisions)
       bisect    Use binary search to find the commit that introduced a bug
       diff      Show changes between commits, commit and working tree, etc
       grep      Print lines matching a pattern
       log       Show commit logs
       show      Show various types of objects
       status    Show the working tree status

    grow, mark and tweak your common history
       branch    List, create, or delete branches
       commit    Record changes to the repository
       merge     Join two or more development histories together
       rebase    Reapply commits on top of another base tip
       reset     Reset current HEAD to the specified state
       switch    Switch branches
       tag       Create, list, delete or verify a tag object signed with GPG

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 command-list.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/command-list.txt b/command-list.txt
index cf8dccb439..a9ac72bef4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -81,7 +81,7 @@ git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
 git-daemon                              synchingrepositories
 git-describe                            mainporcelain
-git-diff                                mainporcelain           history
+git-diff                                mainporcelain           info
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
@@ -150,7 +150,7 @@ git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
-git-reset                               mainporcelain           worktree
+git-reset                               mainporcelain           history
 git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 16/16] Declare both git-switch and git-restore experimental
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (14 preceding siblings ...)
  2019-04-25  9:45     ` [PATCH v3 15/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
@ 2019-04-25  9:46     ` Nguyễn Thái Ngọc Duy
  2019-05-07  2:21     ` [PATCH v3 00/16] Add new command 'restore' Emily Shaffer
  16 siblings, 0 replies; 97+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-04-25  9:46 UTC (permalink / raw)
  To: pclouds
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

These two commands are basically redesigned git-checkout. We will not
have that many opportunities to redo (because we run out of verbs, and
that would also increase maintenance cost).

To play it safe, let's declare the two commands experimental in one or
two releases. If there is a serious flaw in the UI, we could still fix
it. If everything goes well and nobody complains loudly, we can remove
the experimental status by reverting this patch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-restore.txt | 2 ++
 Documentation/git-switch.txt  | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index b608f3f360..d90093f195 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -28,6 +28,8 @@ commit as the restore source.
 See "Reset, restore and revert" in linkgit:git[1] for the differences
 between the three commands.
 
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
 OPTIONS
 -------
 -s <tree>::
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
index f15e5bdcf4..197900363b 100644
--- a/Documentation/git-switch.txt
+++ b/Documentation/git-switch.txt
@@ -29,6 +29,8 @@ Switching branches does not require a clean index and working tree
 however if the operation leads to loss of local changes, unless told
 otherwise with `--discard-changes` or `--merge`.
 
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
 OPTIONS
 -------
 <branch>::
-- 
2.21.0.854.ge34a79f761


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 00/16] Add new command 'restore'
  2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
                       ` (15 preceding siblings ...)
  2019-04-25  9:46     ` [PATCH v3 16/16] Declare both git-switch and git-restore experimental Nguyễn Thái Ngọc Duy
@ 2019-05-07  2:21     ` Emily Shaffer
  2019-05-07  4:57       ` Junio C Hamano
  2019-05-07 10:36       ` Duy Nguyen
  16 siblings, 2 replies; 97+ messages in thread
From: Emily Shaffer @ 2019-05-07  2:21 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

Hiya,

On Thu, Apr 25, 2019 at 04:45:44PM +0700, Nguyễn Thái Ngọc Duy wrote:
> v3 changes are really small
> 
> - gitcli.txt is updated to mention the --staged/--worktree pair, in
>   comparison to --cached/--index which is also mention there.
> 
> - The patch 'rm --staged' is dropped. It could come back. But if it
>   does, it should be in a series where commands that take
>   --cached/--index will now take both --staged/--worktree. Those are
>   'rm', 'apply', 'check-attr', 'grep', 'diff' and maybe 'ls-files'.
>   
>   In other words we support --staged/--worktree everywhere while
>   --cached/--index still remains because of muscle memory. This is
>   of course only a good move if --staged/--worktree is much better
>   than --cached/--index.
> 
> - Since there's a chance this series may end up in 'master' and get
>   released, and I'm still worried that I screwed up somewhere, the last
>   patch declares the two commands experimental for maybe one or two
>   release cycles.
>   
>   If the reception is good, we revert that patch. If something
>   horrible is found, we can still change the default behavior without
>   anybody yelling at us. Or worst case scenario, we remove both
>   commands and declare a failed experiment.
> 
> PS. the intent-to-add support is still not in. But it should be in
> before the experimental status is removed.

I've got a usability comment, as we're giving restore a try within
Google for now. I found myself in a situation where I had accidentally
staged all my changes to tracked files (I think resulting from a stash
pop which generated a merge conflict?) and didn't see a good way to
unstage everything using restore.

I tried out `git restore --staged *` and it tried to restore every build
artifact in my working tree, all of which should be ignored, made a lot of
noisy errors, and left me with my changes still staged.

Then, figuring that I only had a few files, I thought I'd type them
each, with the exception of a .c/.h pair:

g restore --staged builtin/log.c builtin/walken.c revision.*

Because I had build artifacts present, this vomited while trying to
unstage revision.o, and again left me with all my changes still staged.
revision.o is validly ignored:

$ g check-ignore revision.o                               
revision.o

Finally explicitly naming each file which I needed to restore worked,
but I have very little interest in doing this for more than a handful of
files, especially since the output of `git status` is not easy to paste
into the command line (due to the "modified:" etc marker).

I was definitely still able to make it do what I wanted with `git reset
HEAD` but thought you may be interested. It'd be cool to have a hint
which advises how you can unstage everything, or else to pay attention
to the gitignore and not try to unstage those files, or else to have a
command to unstage everything. (I looked for one by trying `git restore
--staged -a` but that doesn't exist :) )

Thanks!
Emily

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 00/16] Add new command 'restore'
  2019-05-07  2:21     ` [PATCH v3 00/16] Add new command 'restore' Emily Shaffer
@ 2019-05-07  4:57       ` Junio C Hamano
  2019-05-07 10:36       ` Duy Nguyen
  1 sibling, 0 replies; 97+ messages in thread
From: Junio C Hamano @ 2019-05-07  4:57 UTC (permalink / raw)
  To: Emily Shaffer
  Cc: Nguyễn Thái Ngọc Duy, Johannes.Schindelin, git,
	jacob.keller, newren, rybak.a.v, sunshine

Emily Shaffer <emilyshaffer@google.com> writes:

> Then, figuring that I only had a few files, I thought I'd type them
> each, with the exception of a .c/.h pair:
>
> g restore --staged builtin/log.c builtin/walken.c revision.*
>
> Because I had build artifacts present, this vomited while trying to
> unstage revision.o, and again left me with all my changes still staged.
> revision.o is validly ignored:
>
> $ g check-ignore revision.o                               
> revision.o

Often a claim "validly ignored" by users needs to be taken with a
grain of salt.  When it is in the index, it is no longer ignored.

Assuming that you do not have revision.o in the index, and if
"restore" is still "checkout -- <pathspec>" in disguise, then I
think you could ask "git" not your shell to expand the asterisk, by
protecting it from the shell, i.e.

	$ git restore --staged revision.\*

and the pathspec should not match untracked revision.o.  And if that
does not work, you found a bug in restore, I think ;-).

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 00/16] Add new command 'restore'
  2019-05-07  2:21     ` [PATCH v3 00/16] Add new command 'restore' Emily Shaffer
  2019-05-07  4:57       ` Junio C Hamano
@ 2019-05-07 10:36       ` Duy Nguyen
  2019-05-07 18:31         ` Emily Shaffer
  1 sibling, 1 reply; 97+ messages in thread
From: Duy Nguyen @ 2019-05-07 10:36 UTC (permalink / raw)
  To: Emily Shaffer
  Cc: Johannes Schindelin, Git Mailing List, Junio C Hamano,
	Jacob Keller, Elijah Newren, Andrei Rybak, Eric Sunshine

On Tue, May 7, 2019 at 9:21 AM Emily Shaffer <emilyshaffer@google.com> wrote:
> I've got a usability comment, as we're giving restore a try within
> Google for now.

Thanks. I thought I was the only guinea pig :D and obviously not a
good one since I know too much (which is not a good thing as a
tester).

> I found myself in a situation where I had accidentally
> staged all my changes to tracked files (I think resulting from a stash
> pop which generated a merge conflict?) and didn't see a good way to
> unstage everything using restore.
>
> I tried out `git restore --staged *` and it tried to restore every build
> artifact in my working tree, all of which should be ignored, made a lot of
> noisy errors, and left me with my changes still staged.

For the record, "git restore --staged :/" should do the trick and it
is documented as an example (but without --staged).

Either way. I think you raise a good point about "*" (or patterns
matching more than expected in general). I need to sleep on it and see
if the old way of handling pattern matching failure is still a good
way to go.

> Finally explicitly naming each file which I needed to restore worked,
> but I have very little interest in doing this for more than a handful of
> files, especially since the output of `git status` is not easy to paste
> into the command line (due to the "modified:" etc marker).

For just a couple files, you could also use "-p" to go through them
and either "a" to restore all or "d" to skip the file. Maybe adding
"-i/--interactive" to git-restore too, just like "git add". Hmm...
-- 
Duyh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 00/16] Add new command 'restore'
  2019-05-07 10:36       ` Duy Nguyen
@ 2019-05-07 18:31         ` Emily Shaffer
  2019-05-08 10:20           ` Duy Nguyen
  0 siblings, 1 reply; 97+ messages in thread
From: Emily Shaffer @ 2019-05-07 18:31 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Johannes Schindelin, Git Mailing List, Junio C Hamano,
	Jacob Keller, Elijah Newren, Andrei Rybak, Eric Sunshine

On Tue, May 07, 2019 at 05:36:56PM +0700, Duy Nguyen wrote:
> On Tue, May 7, 2019 at 9:21 AM Emily Shaffer <emilyshaffer@google.com> wrote:
> > I've got a usability comment, as we're giving restore a try within
> > Google for now.
> 
> Thanks. I thought I was the only guinea pig :D and obviously not a
> good one since I know too much (which is not a good thing as a
> tester).
> 
> > I found myself in a situation where I had accidentally
> > staged all my changes to tracked files (I think resulting from a stash
> > pop which generated a merge conflict?) and didn't see a good way to
> > unstage everything using restore.
> >
> > I tried out `git restore --staged *` and it tried to restore every build
> > artifact in my working tree, all of which should be ignored, made a lot of
> > noisy errors, and left me with my changes still staged.
> 
> For the record, "git restore --staged :/" should do the trick and it
> is documented as an example (but without --staged).

Yeah, this worked, and today I also noted `git restore --staged .`
works, as does Junio's suggestion on the other mail (`git restore
--staged revision.\*`), and quoting the * (`git restore --staged '*'`).
So maybe I didn't think outside the box enough before mailing :)

> 
> Either way. I think you raise a good point about "*" (or patterns
> matching more than expected in general). I need to sleep on it and see
> if the old way of handling pattern matching failure is still a good
> way to go.

I think it's worth considering, especially as `git reset HEAD *` and
`git reset HEAD` both work. (`git restore --staged` complains that it
hasn't got any paths, but reset seems to figure you just mean
everything.) It was a surprising failure to me coming away from years of
using reset.

> 
> > Finally explicitly naming each file which I needed to restore worked,
> > but I have very little interest in doing this for more than a handful of
> > files, especially since the output of `git status` is not easy to paste
> > into the command line (due to the "modified:" etc marker).
> 
> For just a couple files, you could also use "-p" to go through them
> and either "a" to restore all or "d" to skip the file. Maybe adding
> "-i/--interactive" to git-restore too, just like "git add". Hmm...
> -- 
> Duyh

Anyways, maybe it's not a particularly weighty complaint, since there
are still ways to mass unstage, and if I had just RTFM I would have
found them. :) It's a new workflow, after all, and the old one still
works, so it's probably not reasonable to expect it all to just work the
same way with s/reset HEAD/restore --staged/.

Thanks!
 - Emily

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 00/16] Add new command 'restore'
  2019-05-07 18:31         ` Emily Shaffer
@ 2019-05-08 10:20           ` Duy Nguyen
  0 siblings, 0 replies; 97+ messages in thread
From: Duy Nguyen @ 2019-05-08 10:20 UTC (permalink / raw)
  To: Emily Shaffer
  Cc: Johannes Schindelin, Git Mailing List, Junio C Hamano,
	Jacob Keller, Elijah Newren, Andrei Rybak, Eric Sunshine

On Wed, May 8, 2019 at 1:31 AM Emily Shaffer <emilyshaffer@google.com> wrote:
> > > I found myself in a situation where I had accidentally
> > > staged all my changes to tracked files (I think resulting from a stash
> > > pop which generated a merge conflict?) and didn't see a good way to
> > > unstage everything using restore.
> > >
> > > I tried out `git restore --staged *` and it tried to restore every build
> > > artifact in my working tree, all of which should be ignored, made a lot of
> > > noisy errors, and left me with my changes still staged.
> >
> > For the record, "git restore --staged :/" should do the trick and it
> > is documented as an example (but without --staged).
>
> Yeah, this worked, and today I also noted `git restore --staged .`
> works, as does Junio's suggestion on the other mail (`git restore
> --staged revision.\*`), and quoting the * (`git restore --staged '*'`).
> So maybe I didn't think outside the box enough before mailing :)

No it's good. It actually got me thinking. Actually I take that back.
It's bad, I'm quite stuck at thinking here :D

> > Either way. I think you raise a good point about "*" (or patterns
> > matching more than expected in general). I need to sleep on it and see
> > if the old way of handling pattern matching failure is still a good
> > way to go.
>
> I think it's worth considering, especially as `git reset HEAD *` and
> `git reset HEAD` both work. (`git restore --staged` complains that it
> hasn't got any paths, but reset seems to figure you just mean
> everything.) It was a surprising failure to me coming away from years of
> using reset.

Yeah I agree that doing it the "git reset" way makes sense for
--staged because shell expansion is tied to worktree files, and
--staged is completely disconnected from worktree. Shell expansion
cannot get this case right, no point punishing the user because of it.

My problem is "what about the other two cases, --worktree alone or
--worktree with --staged (iow do we care about consistency)? what
about git-checkout? does the old way even make sense for git-checkout?
any better way to do if the reason behind is still valid?". I
obviously haven't worked it all out yet.
-- 
Duy

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 11/16] t: add tests for restore
  2019-04-25  9:45     ` [PATCH v3 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
@ 2019-06-13 15:19       ` SZEDER Gábor
  0 siblings, 0 replies; 97+ messages in thread
From: SZEDER Gábor @ 2019-06-13 15:19 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Johannes.Schindelin, git, gitster, jacob.keller, newren,
	rybak.a.v, sunshine

On Thu, Apr 25, 2019 at 04:45:55PM +0700, Nguyễn Thái Ngọc Duy wrote:

> diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
> new file mode 100755
> index 0000000000..73ea13ede9
> --- /dev/null
> +++ b/t/t2070-restore.sh

> +test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
> +	git init unmerged &&
> +	(
> +		cd unmerged &&
> +		echo one >unmerged &&
> +		echo one >common &&
> +		git add unmerged common &&
> +		git commit -m common &&
> +		git switch -c first &&
> +		echo first >unmerged &&
> +		git commit -am first &&
> +		git switch -c second master &&
> +		echo second >unmerged &&
> +		git commit -am second &&
> +		test_must_fail git merge first &&
> +
> +		echo dirty >>common &&
> +		test_must_fail git restore . &&
> +
> +		git restore --ignore-unmerged --quiet . >output 2>&1 &&
> +		git diff common >diff-output &&
> +		: >empty &&
> +		test_cmp empty output &&
> +		test_cmp empty diff-output

Please use 'test_must_be_empty'.

(and perhaps 'git diff --exit-code common'?  dunno)


^ permalink raw reply	[flat|nested] 97+ messages in thread

end of thread, back to index

Thread overview: 97+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-08 10:16 [PATCH v1 00/11] And new command "restore" Nguyễn Thái Ngọc Duy
2019-03-08 10:16 ` [PATCH v1 01/11] checkout: split part of it to new command 'restore' Nguyễn Thái Ngọc Duy
2019-03-08 18:01   ` Elijah Newren
2019-03-09 12:16     ` Duy Nguyen
2019-03-09 18:27       ` Elijah Newren
2019-04-09 12:18         ` Duy Nguyen
2019-03-25  9:53     ` Duy Nguyen
2019-03-25 15:51       ` Elijah Newren
2019-03-13  9:17   ` Johannes Schindelin
2019-03-08 10:16 ` [PATCH v1 02/11] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
2019-03-09 18:13   ` Elijah Newren
2019-03-10  7:58   ` Eric Sunshine
2019-03-08 10:16 ` [PATCH v1 03/11] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
2019-03-09 18:35   ` Elijah Newren
2019-03-08 10:16 ` [PATCH v1 04/11] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
2019-03-09 18:37   ` Elijah Newren
2019-03-08 10:16 ` [PATCH v1 05/11] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
2019-03-08 10:16 ` [PATCH v1 06/11] restore: add --worktree and --index Nguyễn Thái Ngọc Duy
2019-03-09 18:52   ` Elijah Newren
2019-03-10 20:03     ` Eric Sunshine
2019-03-13 10:02     ` Duy Nguyen
2019-03-13 22:42     ` Junio C Hamano
2019-03-08 10:16 ` [PATCH v1 07/11] restore: default to --source=HEAD when only --index is specified Nguyễn Thái Ngọc Duy
2019-03-09 18:58   ` Elijah Newren
2019-03-08 10:16 ` [PATCH v1 08/11] restore: support --patch Nguyễn Thái Ngọc Duy
2019-03-09 19:02   ` Elijah Newren
2019-03-08 10:16 ` [PATCH v1 09/11] t: add tests for restore Nguyễn Thái Ngọc Duy
2019-03-09 16:53   ` Andrei Rybak
2019-03-13  9:13   ` Johannes Schindelin
2019-03-13  9:20     ` Duy Nguyen
2019-03-13 22:17       ` Johannes Schindelin
2019-03-14  4:57         ` Duy Nguyen
2019-03-14  5:45   ` Junio C Hamano
2019-03-14  5:55     ` Junio C Hamano
2019-03-08 10:16 ` [PATCH v1 10/11] completion: support restore Nguyễn Thái Ngọc Duy
2019-03-09 19:16   ` Elijah Newren
2019-03-11 15:22     ` Duy Nguyen
2019-03-11 15:39       ` Duy Nguyen
2019-03-11 18:28         ` Elijah Newren
2019-03-08 10:16 ` [PATCH v1 11/11] doc: promote "git restore" Nguyễn Thái Ngọc Duy
2019-03-09 19:37   ` Elijah Newren
2019-03-11 14:11     ` Randall S. Becker
2019-03-11 14:36       ` Duy Nguyen
2019-03-13  4:58         ` Junio C Hamano
2019-04-11 10:55           ` Duy Nguyen
2019-03-10 11:19 ` [PATCH v1 00/11] And new command "restore" Duy Nguyen
2019-03-10 22:45   ` Jacob Keller
2019-03-11 16:01   ` Elijah Newren
2019-03-13 22:58   ` Junio C Hamano
2019-03-14  3:49     ` Duy Nguyen
2019-04-11 13:12 ` [PATCH v2 00/16] Add new command 'restore' Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 12/16] completion: support restore Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 15/16] rm: add --staged as alias for --cached Nguyễn Thái Ngọc Duy
2019-04-11 13:12   ` [PATCH v2 16/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
2019-04-12  5:14   ` [PATCH v2 00/16] Add new command 'restore' Junio C Hamano
2019-04-13 10:39     ` Duy Nguyen
2019-04-17 10:04   ` Duy Nguyen
2019-04-18  0:38     ` Junio C Hamano
2019-04-18  9:40       ` Duy Nguyen
2019-04-18 10:03       ` Johannes Schindelin
2019-04-18 10:20         ` Duy Nguyen
2019-04-25  9:45   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 01/16] checkout: split part of it to " Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 02/16] restore: take tree-ish from --source option instead Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 03/16] restore: make pathspec mandatory Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 04/16] restore: disable overlay mode by default Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 05/16] checkout: factor out worktree checkout code Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 06/16] restore: add --worktree and --staged Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 07/16] restore: reject invalid combinations with --staged Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 08/16] restore: default to --source=HEAD when only --staged is specified Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 09/16] restore: replace --force with --ignore-unmerged Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 10/16] restore: support --patch Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 11/16] t: add tests for restore Nguyễn Thái Ngọc Duy
2019-06-13 15:19       ` SZEDER Gábor
2019-04-25  9:45     ` [PATCH v3 12/16] completion: support restore Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 13/16] user-manual.txt: prefer 'merge --abort' over 'reset --hard' Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 14/16] doc: promote "git restore" Nguyễn Thái Ngọc Duy
2019-04-25  9:45     ` [PATCH v3 15/16] help: move git-diff and git-reset to different groups Nguyễn Thái Ngọc Duy
2019-04-25  9:46     ` [PATCH v3 16/16] Declare both git-switch and git-restore experimental Nguyễn Thái Ngọc Duy
2019-05-07  2:21     ` [PATCH v3 00/16] Add new command 'restore' Emily Shaffer
2019-05-07  4:57       ` Junio C Hamano
2019-05-07 10:36       ` Duy Nguyen
2019-05-07 18:31         ` Emily Shaffer
2019-05-08 10:20           ` Duy Nguyen

git@vger.kernel.org list mirror (unofficial, one of many)

Archives are clonable:
	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.org/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox