All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Hugo Sales via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Hugo Sales <hugo@hsal.es>, Hugo Sales <hugo@hsal.es>
Subject: [PATCH] Add `restore.defaultLocation` option
Date: Sat, 11 Mar 2023 23:42:33 +0000	[thread overview]
Message-ID: <pull.1467.git.git.1678578153640.gitgitgadget@gmail.com> (raw)

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.

Signed-off-by: Hugo Sales <hugo@hsal.es>
---
    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@gmail.com, Jeff King peff@peff.net,
    Victoria Dye vdye@github.com
    
    ------------------------------------------------------------------------
    
    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

             reply	other threads:[~2023-03-11 23:42 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-11 23:42 Hugo Sales via GitGitGadget [this message]
2023-03-12 21:32 ` [PATCH] Add `restore.defaultLocation` option Junio C Hamano
2023-03-13 18:02   ` Junio C Hamano
2023-03-13 23:11 ` Junio C Hamano
2023-03-14 23:51   ` Hugo Sales
2023-03-26 10:53     ` Hugo Sales

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.1467.git.git.1678578153640.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=hugo@hsal.es \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.