All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thomas Gummerer <t.gummerer@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>,
	"Elijah Newren" <newren@gmail.com>,
	"Eric Sunshine" <sunshine@sunshineco.com>,
	"Thomas Gummerer" <t.gummerer@gmail.com>
Subject: [PATCH v3 7/8] checkout: introduce --{,no-}overlay option
Date: Tue,  8 Jan 2019 21:52:24 +0000	[thread overview]
Message-ID: <20190108215225.3077-8-t.gummerer@gmail.com> (raw)
In-Reply-To: <20190108215225.3077-1-t.gummerer@gmail.com>

Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.

Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.

Note that 'git checkout -p <tree-ish> -- [<pathspec>]' already works
this way, so no changes are needed for the patch mode.  We disallow
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.

Untracked files are not affected by this change, so 'git checkout
--no-overlay HEAD -- untracked' will not remove untracked from the
working tree.  This is so e.g. 'git checkout --no-overlay HEAD -- dir/'
doesn't delete all untracked files in dir/, but rather just resets the
state of files that are known to git.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 Documentation/git-checkout.txt | 10 ++++++
 builtin/checkout.c             | 66 +++++++++++++++++++++++++++++-----
 t/t2025-checkout-no-overlay.sh | 47 ++++++++++++++++++++++++
 t/t9902-completion.sh          |  1 +
 4 files changed, 116 insertions(+), 8 deletions(-)
 create mode 100755 t/t2025-checkout-no-overlay.sh

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 801de2f764..24e52b01e1 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -260,6 +260,9 @@ the conflicted merge in the specified paths.
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+Note that this option uses the no overlay mode by default (see also
+`--[no-]overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -276,6 +279,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 	Just like linkgit:git-submodule[1], this will detach the
 	submodules HEAD.
 
+--[no-]overlay::
+	In the default overlay mode, `git checkout` never
+	removes files from the index or the working tree.  When
+	specifying `--no-overlay`, files that appear in the index and
+	working tree, but not in <tree-ish> are removed, to make them
+	match <tree-ish> exactly.
+
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 32c4b7f897..0c5fe948ef 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -44,6 +44,7 @@ struct checkout_opts {
 	int ignore_skipworktree;
 	int ignore_other_worktrees;
 	int show_progress;
+	int overlay_mode;
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -132,7 +133,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
 	return pos;
 }
 
-static int check_stage(int stage, const struct cache_entry *ce, int pos)
+static int check_stage(int stage, const struct cache_entry *ce, int pos,
+		       int overlay_mode)
 {
 	while (pos < active_nr &&
 	       !strcmp(active_cache[pos]->name, ce->name)) {
@@ -140,6 +142,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos)
 			return 0;
 		pos++;
 	}
+	if (!overlay_mode)
+		return 0;
 	if (stage == 2)
 		return error(_("path '%s' does not have our version"), ce->name);
 	else
@@ -165,7 +169,7 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 }
 
 static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
-			  const struct checkout *state)
+			  const struct checkout *state, int overlay_mode)
 {
 	while (pos < active_nr &&
 	       !strcmp(active_cache[pos]->name, ce->name)) {
@@ -173,6 +177,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 			return checkout_entry(active_cache[pos], state, NULL);
 		pos++;
 	}
+	if (!overlay_mode) {
+		unlink_entry(ce);
+		return 0;
+	}
 	if (stage == 2)
 		return error(_("path '%s' does not have our version"), ce->name);
 	else
@@ -247,9 +255,9 @@ static int checkout_merged(int pos, const struct checkout *state)
 	return status;
 }
 
-static void mark_ce_for_checkout(struct cache_entry *ce,
-				 char *ps_matched,
-				 const struct checkout_opts *opts)
+static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
+					 char *ps_matched,
+					 const struct checkout_opts *opts)
 {
 	ce->ce_flags &= ~CE_MATCHED;
 	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
@@ -281,6 +289,25 @@ static void mark_ce_for_checkout(struct cache_entry *ce,
 		ce->ce_flags |= CE_MATCHED;
 }
 
+static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
+					    char *ps_matched,
+					    const struct checkout_opts *opts)
+{
+	ce->ce_flags &= ~CE_MATCHED;
+	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+		return;
+	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+		ce->ce_flags |= CE_MATCHED;
+		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
+			/*
+			 * In overlay mode, but the path is not in
+			 * tree-ish, which means we should remove it
+			 * from the index and the working tree.
+			 */
+			ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE;
+	}
+}
+
 static int checkout_paths(const struct checkout_opts *opts,
 			  const char *revision)
 {
@@ -332,7 +359,14 @@ static int checkout_paths(const struct checkout_opts *opts,
 	 * to be checked out.
 	 */
 	for (pos = 0; pos < active_nr; pos++)
-		mark_ce_for_checkout(active_cache[pos], ps_matched, opts);
+		if (opts->overlay_mode)
+			mark_ce_for_checkout_overlay(active_cache[pos],
+						     ps_matched,
+						     opts);
+		else
+			mark_ce_for_checkout_no_overlay(active_cache[pos],
+							ps_matched,
+							opts);
 
 	if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
 		free(ps_matched);
@@ -353,7 +387,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 			if (opts->force) {
 				warning(_("path '%s' is unmerged"), ce->name);
 			} else if (opts->writeout_stage) {
-				errs |= check_stage(opts->writeout_stage, ce, pos);
+				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
 			} else if (opts->merge) {
 				errs |= check_stages((1<<2) | (1<<3), ce, pos);
 			} else {
@@ -380,12 +414,14 @@ static int checkout_paths(const struct checkout_opts *opts,
 				continue;
 			}
 			if (opts->writeout_stage)
-				errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
+				errs |= checkout_stage(opts->writeout_stage, ce, pos, &state, opts->overlay_mode);
 			else if (opts->merge)
 				errs |= checkout_merged(pos, &state);
 			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
+	remove_marked_cache_entries(&the_index, 1);
+	remove_scheduled_dirs();
 	errs |= finish_delayed_checkout(&state);
 
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
@@ -547,6 +583,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,
 	 * opts->show_progress only impacts output so doesn't require a merge
 	 */
 
+	/*
+	 * opts->overlay_mode cannot be used with switching branches so is
+	 * not tested here
+	 */
+
 	/*
 	 * If we aren't creating a new branch any changes or updates will
 	 * happen in the existing branch.  Since that could only be updating
@@ -1183,6 +1224,10 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
 
+	if (!opts->overlay_mode)
+		die(_("'%s' cannot be used with switching branches"),
+		    "--no-overlay");
+
 	if (opts->writeout_stage)
 		die(_("'%s' cannot be used with switching branches"),
 		    "--ours/--theirs");
@@ -1271,6 +1316,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			    "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_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
@@ -1279,6 +1325,7 @@ int cmd_checkout(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);
 
@@ -1302,6 +1349,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
 
+	if (opts.overlay_mode == 1 && opts.patch_mode)
+		die(_("-p and --overlay are mutually exclusive"));
+
 	/*
 	 * 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
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
new file mode 100755
index 0000000000..76330cb5ab
--- /dev/null
+++ b/t/t2025-checkout-no-overlay.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	git commit --allow-empty -m "initial"
+'
+
+test_expect_success 'checkout --no-overlay deletes files not in <tree-ish>' '
+	>file &&
+	mkdir dir &&
+	>dir/file1 &&
+	git add file dir/file1 &&
+	git checkout --no-overlay HEAD -- file &&
+	test_path_is_missing file &&
+	test_path_is_file dir/file1
+'
+
+test_expect_success 'checkout --no-overlay removing last file from directory' '
+	git checkout --no-overlay HEAD -- dir/file1 &&
+	test_path_is_missing dir
+'
+
+test_expect_success 'checkout -p --overlay is disallowed' '
+	test_must_fail git checkout -p --overlay HEAD 2>actual &&
+	test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual
+'
+
+test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
+	test_commit file1 file1 &&
+	test_commit file2 file2 &&
+	git rm --cached file1 &&
+	echo 1234 >file1 &&
+	F1=$(git rev-parse HEAD:file1) &&
+	F2=$(git rev-parse HEAD:file2) &&
+	{
+		echo "100644 $F1 1	file1" &&
+		echo "100644 $F2 2	file1"
+	} | git update-index --index-info &&
+	test_path_is_file file1 &&
+	git checkout --theirs --no-overlay -- file1 &&
+	test_path_is_missing file1
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index d01ad8eb25..5758fffa0d 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1436,6 +1436,7 @@ test_expect_success 'double dash "git checkout"' '
 	--progress Z
 	--no-quiet Z
 	--no-... Z
+	--overlay Z
 	EOF
 '
 
-- 
2.20.1.153.gd81d796ee0


  parent reply	other threads:[~2019-01-08 21:52 UTC|newest]

Thread overview: 115+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-09 20:04 [PATCH 0/8] introduce no-overlay and cached mode in git checkout Thomas Gummerer
2018-12-09 20:04 ` [PATCH 1/8] move worktree tests to t24* Thomas Gummerer
2018-12-10  3:48   ` Junio C Hamano
2018-12-10 15:32   ` Duy Nguyen
2018-12-11 21:50     ` Thomas Gummerer
2018-12-12 13:26       ` Eric Sunshine
2018-12-12 17:07         ` Duy Nguyen
2018-12-09 20:04 ` [PATCH 2/8] entry: factor out unlink_entry function Thomas Gummerer
2018-12-10 15:49   ` Duy Nguyen
2018-12-10 17:23     ` Elijah Newren
2018-12-10 17:27       ` Duy Nguyen
2018-12-11  2:23     ` Junio C Hamano
2018-12-20 13:36     ` Thomas Gummerer
2018-12-09 20:04 ` [PATCH 3/8] entry: support CE_WT_REMOVE flag in checkout_entry Thomas Gummerer
2018-12-10 15:58   ` Duy Nguyen
2018-12-11  2:28     ` Junio C Hamano
2018-12-12  6:16       ` Duy Nguyen
2018-12-12  7:36         ` Junio C Hamano
2018-12-10 17:49   ` Elijah Newren
2018-12-11 22:00     ` Thomas Gummerer
2018-12-09 20:04 ` [PATCH 4/8] read-cache: add invalidate parameter to remove_marked_cache_entries Thomas Gummerer
2018-12-10 16:08   ` Duy Nguyen
2018-12-10 18:09     ` Elijah Newren
2018-12-10 18:19       ` Duy Nguyen
2018-12-10 18:25         ` Elijah Newren
2018-12-10 18:33           ` Duy Nguyen
2018-12-10 18:47             ` Elijah Newren
2018-12-11 21:59       ` Thomas Gummerer
2018-12-11  2:42     ` Junio C Hamano
2018-12-09 20:04 ` [PATCH 5/8] checkout: introduce --{,no-}overlay option Thomas Gummerer
2018-12-10 16:42   ` Duy Nguyen
2018-12-11 22:42     ` Thomas Gummerer
2018-12-10 18:19   ` Elijah Newren
2018-12-11  3:07     ` Junio C Hamano
2018-12-11  6:04       ` Elijah Newren
2018-12-11 22:07       ` Thomas Gummerer
2018-12-09 20:04 ` [PATCH 6/8] checkout: add --cached option Thomas Gummerer
2018-12-10 16:49   ` Duy Nguyen
2018-12-11  3:13     ` Junio C Hamano
2018-12-11  6:12       ` Elijah Newren
2018-12-11 19:23         ` Duy Nguyen
2019-01-31  5:54           ` Duy Nguyen
2019-01-31 19:05             ` Junio C Hamano
2019-02-01  6:48               ` Duy Nguyen
2019-02-01 17:57                 ` Junio C Hamano
2019-02-02 10:57                   ` Duy Nguyen
2019-02-19  4:20                   ` Duy Nguyen
2019-02-19 14:42                     ` Elijah Newren
2019-02-19 14:57                       ` Duy Nguyen
2019-02-19 19:07                       ` Junio C Hamano
2019-02-19 22:24                         ` Elijah Newren
2019-02-19 22:36                           ` Junio C Hamano
2019-02-19 23:00                             ` Elijah Newren
2019-02-20  1:53                             ` Duy Nguyen
2019-02-19 19:02                     ` Junio C Hamano
2019-02-19 19:10                       ` Junio C Hamano
2019-02-19 22:04                         ` Elijah Newren
2019-02-19 22:29                           ` Junio C Hamano
2019-02-20  2:32                             ` Duy Nguyen
2019-02-20  3:52                           ` Duy Nguyen
2019-02-20  2:19                         ` Duy Nguyen
2019-02-19 22:13                       ` Elijah Newren
2019-02-19 22:33                         ` Junio C Hamano
2018-12-10 18:42   ` Elijah Newren
2018-12-11 22:18     ` Thomas Gummerer
2018-12-09 20:04 ` [PATCH 7/8] checkout: allow ignoring unmatched pathspec Thomas Gummerer
2018-12-10 16:51   ` Duy Nguyen
2018-12-11 22:23     ` Thomas Gummerer
2018-12-10 20:25   ` Elijah Newren
2018-12-11 22:36     ` Thomas Gummerer
2018-12-09 20:04 ` [PATCH 8/8] stash: use git checkout --no-overlay Thomas Gummerer
2018-12-10 20:26   ` Elijah Newren
2018-12-10  3:47 ` [PATCH 0/8] introduce no-overlay and cached mode in git checkout Junio C Hamano
2018-12-20  8:43   ` Thomas Gummerer
2018-12-10 17:06 ` Duy Nguyen
2018-12-10 17:18 ` Elijah Newren
2018-12-10 18:37   ` Duy Nguyen
2018-12-11 22:52   ` Thomas Gummerer
2018-12-12  7:28     ` Junio C Hamano
2018-12-20 13:48 ` [PATCH v2 0/8] introduce no-overlay " Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 1/8] move worktree tests to t24* Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 2/8] entry: factor out unlink_entry function Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 3/8] entry: support CE_WT_REMOVE flag in checkout_entry Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 4/8] read-cache: add invalidate parameter to remove_marked_cache_entries Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 5/8] checkout: clarify comment Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 6/8] checkout: factor out mark_cache_entry_for_checkout function Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 7/8] checkout: introduce --{,no-}overlay option Thomas Gummerer
2018-12-23  8:05     ` Duy Nguyen
2018-12-23  9:44       ` Eric Sunshine
2019-01-06 18:18         ` Thomas Gummerer
2018-12-20 13:48   ` [PATCH v2 8/8] checkout: introduce checkout.overlayMode config Thomas Gummerer
2019-01-02 23:39     ` Junio C Hamano
2019-01-06 18:32       ` Thomas Gummerer
2019-01-07 17:00         ` Junio C Hamano
2019-01-08 21:52   ` [PATCH v3 0/8] introduce no-overlay mode in git checkout Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 1/8] move worktree tests to t24* Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 2/8] entry: factor out unlink_entry function Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 3/8] entry: support CE_WT_REMOVE flag in checkout_entry Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 4/8] read-cache: add invalidate parameter to remove_marked_cache_entries Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 5/8] checkout: clarify comment Thomas Gummerer
2019-01-08 21:52     ` [PATCH v3 6/8] checkout: factor out mark_cache_entry_for_checkout function Thomas Gummerer
2019-01-08 21:52     ` Thomas Gummerer [this message]
2019-01-22 23:53       ` [PATCH v3 7/8] checkout: introduce --{,no-}overlay option Jonathan Nieder
2019-01-23 19:05         ` Junio C Hamano
2019-01-23 20:21         ` Thomas Gummerer
2019-01-23 20:47           ` Jonathan Nieder
2019-01-24 22:08             ` Thomas Gummerer
2019-01-23 21:08           ` Junio C Hamano
2019-01-24  1:12             ` Jonathan Nieder
2019-01-24 22:02               ` Thomas Gummerer
2019-01-24 23:02               ` Junio C Hamano
2019-01-25  2:26                 ` Jonathan Nieder
2019-01-25  9:24                   ` Duy Nguyen
2019-02-09 18:54               ` Philip Oakley
2019-01-08 21:52     ` [PATCH v3 8/8] checkout: introduce checkout.overlayMode config Thomas Gummerer

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=20190108215225.3077-8-t.gummerer@gmail.com \
    --to=t.gummerer@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=newren@gmail.com \
    --cc=pclouds@gmail.com \
    --cc=sunshine@sunshineco.com \
    /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.