diff options
authorNguyễn Thái Ngọc Duy <>2019-04-25 09:45:45 (GMT)
committerJunio C Hamano <>2019-05-07 04:04:47 (GMT)
commit46e91b663badd99b3807ab34decfd32f3cbf15e7 (patch)
parent328c6cb853d7237098569de9f94bc3d259846a08 (diff)
checkout: split part of it to new command 'restore'
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 <> Signed-off-by: Junio C Hamano <>
13 files changed, 257 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index c687b92..fb37710 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index ad846dd..a2d3c7e 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 58f18a0..a294652 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -570,7 +570,8 @@ $ git add frotz
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index cbf901e..c25f8a9 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>`. 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.
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
new file mode 100644
index 0000000..b608f3f
--- /dev/null
+++ b/Documentation/git-restore.txt
@@ -0,0 +1,183 @@
+git-restore - Restore working tree files
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+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.
+-s <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.
+ 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.
+ 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.
+ Quiet, suppress feedback messages. Implies `--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`.
+ 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.
+ When restoring files on the working tree from the index,
+ recreate the conflicted merge in the unmerged paths.
+ 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).
+ 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.
+ 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>`.
+ 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.
+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
+$ 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
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 837707a..018ecf4 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.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 00156d6..fbed007 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -210,6 +210,26 @@ people via patch over e-mail.
+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 592e06d..225513e 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -209,6 +209,18 @@ See also and for further
+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.
Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index f85f364..783b077 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 c64e444..6830000 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 0351735..98dc2de 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -38,6 +38,11 @@ static const char * const switch_branch_usage[] = {
+static const char * const restore_usage[] = {
+ N_("git restore [<options>] [<branch>] -- <file>..."),
struct checkout_opts {
int patch_mode;
int quiet;
@@ -1622,3 +1627,24 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
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 13317f4..b9eae1c 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 39582cf..6d439e7 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 },