Message ID | pull.1467.git.git.1678578153640.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add `restore.defaultLocation` option | expand |
"Hugo Sales via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Hugo Sales <hugo@hsal.es> > > This options allows control over which of `--worktree` or `--staged` is > applied when `git restore` is invoked with neither We do not want to do this. Tutorials and documents will be written assuming the official default, and those users who set it to use a different default, only because they are told to use it before they know better, would waste a lot of time wondering why the procedures they read from tutorials and documents would not work for them. Even an expert who is asked to help a novice, who has this variable set, would end up getting confused becaue "git restore" without the explict options does not work as they expect. Individual users are welcome to use "[alias] ri = restore --staged" and this will hurt nobody.
Junio C Hamano <gitster@pobox.com> writes: > "Hugo Sales via GitGitGadget" <gitgitgadget@gmail.com> writes: > >> From: Hugo Sales <hugo@hsal.es> >> >> This options allows control over which of `--worktree` or `--staged` is >> applied when `git restore` is invoked with neither > > We do not want to do this. Tutorials and documents will be written > assuming the official default, ... Well, I think this change is no different from any other configuration option and it may be OK. My initial reaction primarily came from the fact that the choice between the index and the working tree is so fundamental aspect of the behaviour of the command that people who wrote their script would be relying on it not to change. But given that the command is still marked as experimental, as long as the new behaviour is clearly documented to warn those who care *not* to rely on the default behaviour and tell them to instead always give explicitly these "--worktree" and/or "--staged" options, it would be OK. This actually is a more important tangent, but if you think the command invites mistakes from users who forget to give "--staged", it may indicate that the command is too overloaded, and the UI might be improved by removing the feature from this command and instead encouraging people to use "git reset" to futz with the contents of the index. I dunno.
"Hugo Sales via GitGitGadget" <gitgitgadget@gmail.com> writes: > Subject: Re: [PATCH] Add `restore.defaultLocation` option As to the form, perhaps Subject: [PATCH] restore: make it configurable what gets updated cf. Documentation/SubmittingPatches[[describe-changes]] Also on substance, * "option" usually refers to the --option given on the command line; when you mean "configuration variable", it is a misleading word to use. * "restore" command deals with two quite conceptually different locations. Where it gets the contents from (i.e. the source location), and where the contents are used to update (i.e. the destination location). The ".defaultLocation" is a poor name for a configuration variable because it does not convey which one is meant. > From: Hugo Sales <hugo@hsal.es> > > This options allows control over which of `--worktree` or `--staged` is > applied when `git restore` is invoked with neither > > This patch is intended to reduce lost work to accidental `git restore .` > when `git restore --staged .` was intended. We usually describe the status quo (what the current system does), what bad things can happen with the current system, and then what change is proposed to improve the situation, in this order. So the above is backwards. Perhaps like "git restore" takes "--worktree" and/or "--staged" options to specify which one of the working tree files and/or the index entries are updated. With neither options, the command by default updates the working tree files. A user who wanted to reset the index entries from HEAD may by mistake run "git restore" without the "--staged" option. When such a mistake happens, the work made in the working tree files that are not yet added to the index will be forever lost. Introduce the restore.defaultDestination configuration variable, which can be set to one of "both", "index", or "worktree", to help those users who want to set it to "index" to avoid touching the working tree files by mistake. They now force themselves to use the "--worktree" option explicitly when they want to restore the working tree files. or something along that line would make it more like our log messages. Note that even though I wrote the above by guessing what your motivation behind this change is, I do not very much agree with the third paragraph myself. I think a better solution would be to teach these users to use "git reset -- <path>" when they want to reset the index entries, and not run the "git restore" command. > + This > + option can be used to prevent accidentally losing work by running `git > + restore .` when `git restore --staged .` was intended. This is better left out, as it cuts both ways. With it set to 'index', this option will clobber the index entries the user carefully prepared so far with "git add -p" and friends, when the user wanted to update the working tree files (either from the index or from an existing commit) by running "git restore", which will lose work. If we wanted to advertise it as a feature, then the sentence should say something like This variable can be set to 'index' to prevent accidentally losing work ... "can be used" is too vague when you are talking about setting it to a particular value. IOW, setting it to any other value would *NOT* prevent "git restore" from behaving differently from "git resetore --staged". Again, I do not think it is a good idea to sell it as a feature in the first place, as it cuts both ways. > diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt > index 5964810caa4..28165861f55 100644 > --- a/Documentation/git-restore.txt > +++ b/Documentation/git-restore.txt > @@ -14,14 +14,18 @@ SYNOPSIS > > DESCRIPTION > ----------- > -Restore specified paths in the working tree with some contents from a > +Restore specified paths in the working tree or index with some contents from a Shouldn't it be "and/or"? > diff --git a/builtin/checkout.c b/builtin/checkout.c > index a5155cf55c1..5067753030b 100644 > --- a/builtin/checkout.c > +++ b/builtin/checkout.c > @@ -1942,6 +1966,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix) > struct branch_info new_branch_info = { 0 }; > > memset(&opts, 0, sizeof(opts)); > + > opts.accept_ref = 0; > opts.accept_pathspec = 1; > opts.empty_pathspec_ok = 0; Why? > diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh > index 7c43ddf1d99..6e9b06e0bf4 100755 > --- a/t/t2070-restore.sh > +++ b/t/t2070-restore.sh > @@ -137,4 +137,128 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' ' > test_must_fail git rev-parse HEAD:new1 > ' > > +test_expect_success 'restore with restore.defaultLocation unset works as if --worktree given' ' > + test_when_finished git reset --hard HEAD^ && > + test_commit root-unset-restore.defaultLocation && > + test_commit unset-restore.defaultLocation one one && > + > one && Lose SP between ">" and "one", its redirection target. cf. Documentation/CodingGuidelines, look for "Redirection operators should be written with space before, but no space after them." and study the entire paragraph. > + git restore one && > + test -z $(git status --porcelain --untracked-files=no) && This (together with many other uses of "git status" in the new tests) will not catch a segfaulting "git status". Thanks.
I think I addressed your comments in the patch below. > I think a better solution would be to teach > these users to use "git reset -- <path>" when they want to reset the > index entries, and not run the "git restore" command. I read _somewhere_ that `git restore` was the Modern Replacement for `git checkout` and `git reset`, along with `git switch`. I do find it more intuitive, too. Friends I talked with liked this change, as they've also read similar things and lost work due to a missing `--staged`, so they stopped using it. I find `git reset` somewhat confusing, as I use it for `git reset --soft HEAD~` and `git reset --hard <commit-ish>`, so I don't really associate it with the same usage as `git restore`. > +static const char *checkout_default_index_worktree; > +static int git_restore_config(const char *var, const char *value, void *cb) > +{ > + struct checkout_opts *opts = cb; > + > + if (!strcmp(var, "restore.defaultdestination")) { > + git_config_string(&checkout_default_index_worktree, var, value); I'm not sure if this use of `git_config_string` is required, as `value` seems to contain the desired value, but this seems to be how it's done elsewhere. > > + git restore one && > > + test -z $(git status --porcelain --untracked-files=no) && > > This (together with many other uses of "git status" in the new > tests) will not catch a segfaulting "git status". I replaced this with `test_must_be_empty`, I'm not sure if that's what you meant. I wasn't able to find a version that doesn't need to write to a file. I'm aware I'm not supposed to send patches as an attachment, but I spent at least 3 hours today and 3-4 more the day I sent the first patch, and it just doesn't work, so I hope this works From ec878a84db4dc7aabbc5cddf0897b717ac772494 Mon Sep 17 00:00:00 2001 From: Hugo Sales <hugo@hsal.es> Date: Sat, 11 Mar 2023 12:43:34 +0000 Subject: [PATCH v2] restore: add `restore.defaultDestination` to configure what gets updated `git restore` takes `--worktree` and/or `--staged` options to specify which one of the working tree files and/or the index entries are updated. With neither option, the command, by default, updates the working tree files. If a user attempts to reset the index entries from HEAD, they may, by mistake, run `git restore` without the `--staged` option. When such a mistake happens, the work made in the working tree files that are not yet added to the index will be forever lost. This patch is intended to mitigate this. This is a trade-off between lost worktree changes, which may not be present anywhere else, and lost index modifications, which can be recreated. Introduce the `restore.defaultDestination` configuration variable, which can be set to one of "both", "index", or "worktree", useful for users who want to set it to "index" to avoid touching the working tree files by mistake. They now force themselves to use the `--worktree` option explicitly when they want to restore the working tree files. Signed-off-by: Hugo Sales <hugo@hsal.es> --- I think I addressed your comments in the patch below. > I think a better solution would be to teach > these users to use "git reset -- <path>" when they want to reset the > index entries, and not run the "git restore" command. I read _somewhere_ that `git restore` was the Modern Replacement for `git checkout` and `git reset`, along with `git switch`. I do find it more intuite, too. Friends I talked with liked this change, as they've also read similar things and lost work due to a missing `--staged`, so they stopped using it. I find `git reset` somewhat confusing, as I use it for `git reset --soft HEAD~` and `git reset --hard <commit-ish>`, so I don't really associate it with the same usage as `git restore`. > +static const char *checkout_default_index_worktree; > +static int git_restore_config(const char *var, const char *value, void *cb) > +{ > + struct checkout_opts *opts = cb; > + > + if (!strcmp(var, "restore.defaultdestination")) { > + git_config_string(&checkout_default_index_worktree, var, value); I'm not sure if this use of `git_config_string` is required, as `value` seems to contain the desired value, but this seems to be how it's done elsewhere. > > + git restore one && > > + test -z $(git status --porcelain --untracked-files=no) && > > This (together with many other uses of "git status" in the new > tests) will not catch a segfaulting "git status". I replaced this with `test_must_be_empty`, I'm not sure if that's what you meant. I wasn't able to find a version that doesn't need to write to a file. P.S. I hope the following reaches you correctly, as I'm still struggling to connect to mailbox.org with send-email. Documentation/config.txt | 2 + Documentation/config/restore.txt | 16 ++++ Documentation/git-restore.txt | 17 ++-- builtin/checkout.c | 25 ++++++ t/t2070-restore.sh | 143 +++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 Documentation/config/restore.txt diff --git a/Documentation/config.txt b/Documentation/config.txt index 0e93aef862..4359c63794 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -501,6 +501,8 @@ include::config/repack.txt[] include::config/rerere.txt[] +include::config/restore.txt[] + include::config/revert.txt[] include::config/safe.txt[] diff --git a/Documentation/config/restore.txt b/Documentation/config/restore.txt new file mode 100644 index 0000000000..6a06310b4a --- /dev/null +++ b/Documentation/config/restore.txt @@ -0,0 +1,16 @@ +restore.defaultDestination:: + Valid values: "worktree", "staged" or "both". Controls the default + behavior of `git restore` without `--worktree` or `--staged`. If + "worktree", `git restore` without `--worktree` or `--staged` is + equivalent to `git restore --worktree`. If "staged", `git restore` + without `--worktree` or `--staged` is equivalent to `git restore + --staged`. If "both", `git restore` without `--worktree` or `--staged` + is equivalent to `git restore --worktree --staged`. Adding an option + overrides the default, such that if the configuration variable is set to + "staged", specifying `--worktree` will only affect the worktree, not + both. This variable can be set to "staged" to help prevent accidentally + losing modifications to the worktree, caused by running `git restore .` + when `git restore --staged .` was intended. In this case, modifications + to the index would be lost, which could also be a significant amount of + work, so care is highly recommended. + See linkgit:git-restore[1] diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt index 5964810caa..391c367d32 100644 --- a/Documentation/git-restore.txt +++ b/Documentation/git-restore.txt @@ -14,14 +14,18 @@ SYNOPSIS DESCRIPTION ----------- -Restore specified paths in the working tree with some contents from a +Restore specified paths in the working tree and/or index 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 +The command can be used to restore the content in the index with `--staged`, or restore both the working tree and the index with `--staged --worktree`. +The configuration vairable `restore.defaultDestination`, which accepts values +"worktree", "staged" or "both", can be used to control the default behavior for +which flag(s) apply if neither `--staged` nor `--worktree` is supplied. + By default, if `--staged` is given, the contents are restored from `HEAD`, otherwise from the index. Use `--source` to restore from a different commit. @@ -59,9 +63,12 @@ all modified paths. --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. + Specify the restore destination. If neither option is specified, the default + depends on the `'restore.defaultDestination` configuration variable, + which can be "worktree" (the default), "staged" or "both", to control + which of the two flags is assumed if none are given. Specifying + `--worktree` will only restore the worktree. Specifying `--staged` will + only restore the index. Specifying both restores both. -q:: --quiet:: diff --git a/builtin/checkout.c b/builtin/checkout.c index a5155cf55c..4a9131ec29 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1922,6 +1922,29 @@ int cmd_switch(int argc, const char **argv, const char *prefix) return ret; } +static const char *checkout_default_index_worktree; +static int git_restore_config(const char *var, const char *value, void *cb) +{ + struct checkout_opts *opts = cb; + + if (!strcmp(var, "restore.defaultdestination")) { + git_config_string(&checkout_default_index_worktree, var, value); + if (!strcmp(checkout_default_index_worktree, "both")) { + opts->checkout_index = -2; /* default on */ + opts->checkout_worktree = -2; /* default on */ + } else if (!strcmp(checkout_default_index_worktree, "staged")) { + opts->checkout_index = -2; /* default on */ + opts->checkout_worktree = -1; /* default off */ + } else { + opts->checkout_index = -1; /* default off */ + opts->checkout_worktree = -2; /* default on */ + } + return 0; + } + return git_xmerge_config(var, value, NULL); +} + + int cmd_restore(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -1950,6 +1973,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix) opts.checkout_worktree = -2; /* default on */ opts.ignore_unmerged_opt = "--ignore-unmerged"; + git_config(git_restore_config, &opts); + options = parse_options_dup(restore_options); options = add_common_options(&opts, options); options = add_checkout_path_options(&opts, options); diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh index 7c43ddf1d9..c3a35dd878 100755 --- a/t/t2070-restore.sh +++ b/t/t2070-restore.sh @@ -137,4 +137,147 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' ' test_must_fail git rev-parse HEAD:new1 ' +test_expect_success 'restore with restore.defaultDestination unset works as if --worktree given' ' + test_when_finished git reset --hard HEAD^ && + test_commit root-unset-restore.defaultDestination && + test_commit unset-restore.defaultDestination one one && + >one && + + git restore one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^M " && + + >one && + git add one && + git restore --worktree one && + git status --porcelain --untracked-files=no | grep "^M " && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + >one && + git add one && + git restore --worktree --staged one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status +' + +test_expect_success 'restore with restore.defaultDestination set to worktree works as if --worktree given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultDestination && + test_commit root-worktree-restore.defaultDestination && + test_commit worktree-restore.defaultDestination one one && + git config restore.defaultDestination worktree && + >one && + + git restore one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^M " && + + >one && + git add one && + git restore --worktree one && + git status --porcelain --untracked-files=no | grep "^M " && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + >one && + git add one && + git restore --worktree --staged one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status +' + +test_expect_success 'restore with restore.defaultDestination set to staged works as if --staged given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultDestination && + test_commit root-staged-restore.defaultDestination && + test_commit staged-restore.defaultDestination one one && + git config restore.defaultDestination staged && + >one && + + git restore one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --worktree one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore --worktree --staged one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status +' + +test_expect_success 'restore with restore.defaultDestination set to both works as if --worktree --staged given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultDestination && + test_commit root-both-restore.defaultDestination && + test_commit both-restore.defaultDestination one one && + git config restore.defaultDestination both && + >one && + + git restore one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --worktree one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status && + + >one && + git add one && + git restore --worktree --staged one && + git status --porcelain --untracked-files=no >status && + test_must_be_empty status && + rm status +' + test_done
Wanted to add that since `git status` suggests using `git restore --staged` to reset index entries, I'm sure it has happened many times that a user ran `git restore` and lost work
diff --git a/Documentation/config.txt b/Documentation/config.txt index 0e93aef8626..4359c63794e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -501,6 +501,8 @@ include::config/repack.txt[] include::config/rerere.txt[] +include::config/restore.txt[] + include::config/revert.txt[] include::config/safe.txt[] diff --git a/Documentation/config/restore.txt b/Documentation/config/restore.txt new file mode 100644 index 00000000000..479fd13ca24 --- /dev/null +++ b/Documentation/config/restore.txt @@ -0,0 +1,13 @@ +restore.defaultLocation:: + Valid values: "worktree", "staged" or "both". Controls the default + behavior of `git restore` without `--worktree` or `--staged`. If + "worktree", `git restore` without `--worktree` or `--staged` is + equivalent to `git restore --worktree`. If "staged", `git restore` + without `--worktree` or `--staged` is equivalent to `git restore + --staged`. If "both", `git restore` without `--worktree` or `--staged` + is equivalent to `git restore --worktree --staged`. Adding an option + overrides the default, such that if the option is set to "staged", + specifying `--worktree` will only affect the worktree, not both. This + option can be used to prevent accidentally losing work by running `git + restore .` when `git restore --staged .` was intended. + See linkgit:git-restore[1] diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt index 5964810caa4..28165861f55 100644 --- a/Documentation/git-restore.txt +++ b/Documentation/git-restore.txt @@ -14,14 +14,18 @@ SYNOPSIS DESCRIPTION ----------- -Restore specified paths in the working tree with some contents from a +Restore specified paths in the working tree or index 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 +The command can be used to restore the content in the index with `--staged`, or restore both the working tree and the index with `--staged --worktree`. +The config options `restore.defaultLocation`, which accepts values "worktree", +"staged" or "both", can be used to control the default behavior for which +flag(s) apply if neither `--staged` nor `--worktree` is supplied. + By default, if `--staged` is given, the contents are restored from `HEAD`, otherwise from the index. Use `--source` to restore from a different commit. @@ -59,9 +63,12 @@ all modified paths. --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. + Specify the restore location. If neither option is specified, the + default depends on the `'restore.defaultLocation` config option, which + can be "worktree" (the default), "staged" or "both", to control which of + the two flags is assumed if none are given. Specifying `--worktree` will + only restore the worktree. Specifying `--staged` will only restore the + index. Specifying both restores both. -q:: --quiet:: diff --git a/builtin/checkout.c b/builtin/checkout.c index a5155cf55c1..5067753030b 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1922,6 +1922,30 @@ int cmd_switch(int argc, const char **argv, const char *prefix) return ret; } +static const char *checkout_default_index_worktree; +static int git_restore_config(const char *var, const char *value, void *cb) +{ + struct checkout_opts *opts = cb; + + if (!strcmp(var, "restore.defaultlocation")) { + git_config_string(&checkout_default_index_worktree, var, value); + + if (!strcmp(checkout_default_index_worktree, "both")) { + opts->checkout_index = -2; /* default on */ + opts->checkout_worktree = -2; /* default on */ + } else if (!strcmp(checkout_default_index_worktree, "staged")) { + opts->checkout_index = -2; /* default on */ + opts->checkout_worktree = -1; /* default off */ + } else { + opts->checkout_index = -1; /* default off */ + opts->checkout_worktree = -2; /* default on */ + } + return 0; + } + return git_xmerge_config(var, value, NULL); +} + + int cmd_restore(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -1942,6 +1966,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix) struct branch_info new_branch_info = { 0 }; memset(&opts, 0, sizeof(opts)); + opts.accept_ref = 0; opts.accept_pathspec = 1; opts.empty_pathspec_ok = 0; @@ -1950,6 +1975,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix) opts.checkout_worktree = -2; /* default on */ opts.ignore_unmerged_opt = "--ignore-unmerged"; + git_config(git_restore_config, &opts); + options = parse_options_dup(restore_options); options = add_common_options(&opts, options); options = add_checkout_path_options(&opts, options); diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh index 7c43ddf1d99..6e9b06e0bf4 100755 --- a/t/t2070-restore.sh +++ b/t/t2070-restore.sh @@ -137,4 +137,128 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' ' test_must_fail git rev-parse HEAD:new1 ' +test_expect_success 'restore with restore.defaultLocation unset works as if --worktree given' ' + test_when_finished git reset --hard HEAD^ && + test_commit root-unset-restore.defaultLocation && + test_commit unset-restore.defaultLocation one one && + > one && + + git restore one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^M " && + + > one && + git add one && + git restore --worktree one && + git status --porcelain --untracked-files=no | grep "^M " && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + > one && + git add one && + git restore --worktree --staged one && + test -z $(git status --porcelain --untracked-files=no) +' + +test_expect_success 'restore with restore.defaultLocation set to worktree works as if --worktree given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultLocation && + test_commit root-worktree-restore.defaultLocation && + test_commit worktree-restore.defaultLocation one one && + git config restore.defaultLocation worktree && + > one && + + git restore one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^M " && + + > one && + git add one && + git restore --worktree one && + git status --porcelain --untracked-files=no | grep "^M " && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + > one && + git add one && + git restore --worktree --staged one && + test -z $(git status --porcelain --untracked-files=no) +' + +test_expect_success 'restore with restore.defaultLocation set to staged works as if --staged given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultLocation && + test_commit root-staged-restore.defaultLocation && + test_commit staged-restore.defaultLocation one one && + git config restore.defaultLocation staged && + > one && + + git restore one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --worktree one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore --worktree --staged one && + test -z $(git status --porcelain --untracked-files=no) +' + +test_expect_success 'restore with restore.defaultLocation set to both works as if --worktree --staged given' ' + test_when_finished git reset --hard HEAD^ && + test_when_finished git config --unset restore.defaultLocation && + test_commit root-both-restore.defaultLocation && + test_commit both-restore.defaultLocation one one && + git config restore.defaultLocation both && + > one && + + git restore one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git add one && + git restore one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore --staged one && + git status --porcelain --untracked-files=no | grep "^ M" && + + git restore --worktree one && + test -z $(git status --porcelain --untracked-files=no) && + + > one && + git add one && + git restore --worktree --staged one && + test -z $(git status --porcelain --untracked-files=no) +' + + test_done