From: Hugo Sales <hugo@xxxxxxx> 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. Signed-off-by: Hugo Sales <hugo@xxxxxxx> --- Add restore.defaultLocation option 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. CC: Ævar Arnfjörð Bjarmason avarab@xxxxxxxxx, Jeff King peff@xxxxxxxx, Victoria Dye vdye@xxxxxxxxxx ------------------------------------------------------------------------ I tried to send with git send-email, but I'm having problems. My mail provider is mailbox.org and I'm getting Command unknown: 'AUTH' at /usr/lib/git-core/git-send-email line 1691. Apologies if it actually went through. Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1467%2Fsomeonewithpc%2Fmaster-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1467/someonewithpc/master-v1 Pull-Request: https://github.com/git/git/pull/1467 Documentation/config.txt | 2 + Documentation/config/restore.txt | 13 ++++ Documentation/git-restore.txt | 17 +++-- builtin/checkout.c | 27 +++++++ t/t2070-restore.sh | 124 +++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 Documentation/config/restore.txt 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 base-commit: 725f57037d81e24eacfda6e59a19c60c0b4c8062 -- gitgitgadget