All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/19] Add new command "switch"
@ 2019-01-30  9:48 Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                   ` (21 more replies)
  0 siblings, 22 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is the first half of the previous "switch-branch and
restore-files" RFC series [1]. I only focus on switch-branch for now
because it's already getting long and I still have some work to do on
restore-files.

The command is, as the name implies, for switching branches. And it's
supposed to be a (hopefully better) alternative for "git checkout". Head
to 08/19 for the man page, which describes the command (with
examples!) better than I do here.

I think I've addressed most of the issues from the last round. The
only one left is adding options similar to "git reset" (but with
better names this time than --soft and --hard, hopefully). I think
that could be done in a follow-up series.

For a complete picture, you would need to consider "git restore" too
(the new name of "git restore-files"). Full series is available [2]
though I still need to work on the "restore from a tree" part of
that command and thinking whether "restore -p" vs "reset -p" could be
confusing...

This series is built on top of master + tg/checkout-no-overlay.

PS. One of the things I like about this, after using it for some time,
is the "no ambiguation" UI design gives better completion support
(much less useless completion candidates), which makes life much
better.

[1] https://public-inbox.org/git/20181129215850.7278-1-pclouds@gmail.com/
[2] https://gitlab.com/pclouds/git/commits/switch-and-restore

Nguyễn Thái Ngọc Duy (19):
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command switch
  switch: better names for -b and -B
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: no implicit dwim, use --guess to dwim
  switch: no worktree status unless real branch switch happens
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |   8 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                |  48 +-
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   4 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt                  | 236 +++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  18 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  52 +-
 Makefile                                      |   1 +
 advice.c                                      |  11 +-
 builtin.h                                     |   1 +
 builtin/checkout.c                            | 464 +++++++++---------
 builtin/clone.c                               |   2 +-
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  26 +
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   3 +-
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |   4 +-
 t/t2060-switch.sh                             |  91 ++++
 39 files changed, 770 insertions(+), 363 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh

-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH 01/19] git-checkout.txt: fix one syntax line
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ce7d38b8b7..bc7fbdb8d2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 02/19] doc: document --overwrite-ignore
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 5 +++++
 Documentation/git-merge.txt    | 4 ++++
 2 files changed, 9 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index bc7fbdb8d2..9ac01f7967 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -270,6 +270,11 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--[no-]overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use --no-overwrite-ignore to abort
+	the operation when the new branch contains ignored files.
+
 --[no-]recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..0480e9d0f0 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,10 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--[no-]overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use --no-overwrite-ignore to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH 04/19] checkout: factor out some code in parse_branchname_arg()
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f63d864a91..12b7e08d4e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1068,6 +1068,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1075,10 +1103,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1200,26 +1226,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 05/19] checkout: make "opts" in cmd_checkout() a pointer
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 12b7e08d4e..66f99e788d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1326,82 +1326,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1409,14 +1410,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1425,7 +1426,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1444,56 +1445,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1512,6 +1513,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 66f99e788d..5914dd3619 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -46,6 +46,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -57,6 +59,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1329,8 +1332,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1355,12 +1357,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1378,6 +1380,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1386,7 +1389,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1394,9 +1397,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 07/19] checkout: split options[] array in three pieces
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (5 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  3 +-
 3 files changed, 78 insertions(+), 24 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5914dd3619..8ea1349cce 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1327,15 +1327,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1345,34 +1361,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1386,6 +1417,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index e2f3eaed07..76eb5c211b 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 14fe32428e..6846a5b1e0 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -237,7 +237,8 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 
-extern struct option *parse_options_concat(struct option *a, struct option *b);
+struct option *parse_options_dup(const struct option *a);
+struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (6 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-31  0:50   ` Eric Sunshine
  2019-01-31 13:09   ` SZEDER Gábor
  2019-01-30  9:48 ` [PATCH 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                   ` (13 subsequent siblings)
  21 siblings, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and
something-to-checkout-paths. The good old "git checkout" command is
still here and will be until all (or most of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                        |   1 +
 Documentation/config/advice.txt   |  13 +-
 Documentation/config/branch.txt   |   4 +-
 Documentation/config/checkout.txt |   9 +-
 Documentation/config/diff.txt     |   3 +-
 Documentation/git-checkout.txt    |   4 +
 Documentation/git-switch.txt      | 236 ++++++++++++++++++++++++++++++
 Documentation/gitattributes.txt   |   3 +-
 Documentation/githooks.txt        |   8 +-
 Makefile                          |   1 +
 builtin.h                         |   1 +
 builtin/checkout.c                |  60 ++++++--
 command-list.txt                  |   1 +
 git.c                             |   1 +
 14 files changed, 318 insertions(+), 27 deletions(-)
 create mode 100644 Documentation/git-switch.txt

diff --git a/.gitignore b/.gitignore
index 0d77ea5894..8e2b89047f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index 73380a8d86..fc211eca58 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 9ac01f7967..31c6cbef19 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -556,6 +556,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..953c9747b2
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,236 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--guess] <branch>
+'git switch' [<options>] --detach [<start_point>>]
+'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]
+
+DESCRIPTION
+-----------
+Switch to a specified branch and update files in the working tree to
+match it. Optionally a new branch could be created with either `-c` or
+`-C`, or detach from any branch with `--detach`, along with switching.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new_branch>::
+	Name for the new branch.
+
+<start_point>::
+	The name of a commit at which to switch to before creating a
+	new branch or detach from.
++
+You can use the `"@{-N}"` syntax to refer to the N-th last
+branch/commit switched to "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `"@{-1}`.
++
+As a special case, you may use `"A...B"` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new_branch>::
+--create <new_branch>::
+	Create a new branch named <new_branch> and start it at
+	<start_point>; see linkgit:git-branch[1] for details.
+
+-C <new_branch>::
+--force-create <new_branch>::
+	Creates the branch <new_branch> and start it at <start_point>;
+	if it already exists, then reset it to <start_point>. This is
+	equivalent to running "git branch" with "-f"; see
+	linkgit:git-branch[1] for details.
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+-g::
+--guess::
+	If <branch> is not found but there does exist a tracking
+	branch in exactly one remote (call it <remote>) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+-f::
+--force::
+	Proceed even if the index or the working tree differs from
+	HEAD.  This is used to throw away local changes.
+
+--[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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See "--track" in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use "hack" as
+the local branch when branching off of "origin/hack" (or
+"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	branch.autoSetupMerge configuration variable is true.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+--orphan <new_branch>::
+	Create a new 'orphan' branch, named <new_branch>, started from
+	<start_point> and switch to it. See explanation of the same
+	option in linkgit:git-checkout[1] for details.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--[no-]recurse-submodules::
+	Using --recurse-submodules will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach the
+	submodules HEAD.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name, use `-g`:
+
+------------
+$ git switch -g new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit "HEAD~3" for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index b8392fc330..5731caa4c6 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 959044347e..e257a7810a 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -161,12 +161,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -402,7 +403,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 6e8d017e8e..0e5b29390c 100644
--- a/Makefile
+++ b/Makefile
@@ -789,6 +789,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8ea1349cce..2ac53b4302 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1396,33 +1401,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1555,3 +1552,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 0ce0e13f0f..31b8e8d6a1 100644
--- a/git.c
+++ b/git.c
@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 09/19] switch: better names for -b and -B
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (7 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-31  7:03   ` Eric Sunshine
  2019-01-30  9:48 ` [PATCH 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout
out. Move -b/-B to cmd_checkout() and new -c/-C with the same
functionality in cmd_switch_branch()

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2ac53b4302..2251883a88 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a new branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 10/19] switch: remove -l
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (8 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2251883a88..db9a2692db 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1357,7 +1357,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1558,6 +1557,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 11/19] switch: stop accepting pathspec
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (9 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index db9a2692db..7906e07352 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -52,6 +52,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1162,10 +1163,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1199,11 +1206,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1248,7 +1256,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1564,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1591,6 +1600,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 12/19] switch: reject "do nothing" case
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (10 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 7906e07352..9a58fccd71 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1319,6 +1320,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("nothing to do"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1572,6 +1579,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1601,6 +1609,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 13/19] switch: only allow explicit detached HEAD
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (11 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9a58fccd71..69df3a1547 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -44,6 +44,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1326,6 +1327,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("nothing to do"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die(_("a branch is expected, got %s"), new_branch_info->name);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1581,6 +1590,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1610,6 +1620,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 14/19] switch: add short option for --detach
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (12 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 69df3a1547..49c7ac211e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1381,7 +1381,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 15/19] switch: no implicit dwim, use --guess to dwim
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (13 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Similar to automatic detach, this behavior could be confusing because
it can sometimes create a new branch without a user asking it to,
especially when the user is still not aware about this feature.

In the future, perhaps we could have a config key to disable these
safety nets and let 'switch' do automatic detach or dwim
again. But that will be opt-in after the user knows what is what. For
now give a short option if you want to use it often.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 37 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++--------
 2 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 31c6cbef19..bcb7822c27 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	<branch>.
 +
 If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
-equivalent to
+exactly one remote (call it <remote>) with a matching name and
+--no-guess is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit <branch>, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	branch.autoSetupMerge configuration variable is true.
 
+--[no-]guess::
+	If <branch> is not found but there does exist a tracking
+	branch in exactly one remote (call it <remote>) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -284,10 +295,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach the
 	submodules HEAD.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --[no-]overlay::
 	In the default overlay mode, `git checkout` never
 	removes files from the index or the working tree.  When
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 49c7ac211e..cdd07f6aba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -52,7 +52,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 
@@ -1388,8 +1388,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1426,7 +1424,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1441,7 +1438,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1501,7 +1497,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1582,12 +1578,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1612,12 +1610,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a new branch")),
+		OPT_BOOL('g', "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 16/19] switch: no worktree status unless real branch switch happens
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (14 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 127 +++---------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 9 insertions(+), 140 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index fc211eca58..2fe24049fe 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -17,14 +17,6 @@ and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
 
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
-
 checkout.overlayMode::
 	In the default overlay mode, `git checkout` never
 	removes files from the index or the working tree.  When
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cdd07f6aba..8b4aabd6db 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -25,8 +25,6 @@
 #include "submodule.h"
 #include "advice.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -55,11 +53,7 @@ struct checkout_opts {
 	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -556,104 +550,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1011,6 +907,8 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
+
 	memset(&old_branch_info, 0, sizeof(old_branch_info));
 	old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
 	if (old_branch_info.path)
@@ -1027,16 +925,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1058,11 +952,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 {
 	struct checkout_opts *opts = cb;
 
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "checkout.overlaymode")) {
 		opts->overlay_mode = git_config_bool(var, value);
 		return 0;
@@ -1587,6 +1476,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1620,6 +1510,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 17/19] t: add tests for switch
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (15 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-31  7:18   ` Eric Sunshine
  2019-01-30  9:48 ` [PATCH 18/19] completion: support switch Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2060-switch.sh | 91 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100755 t/t2060-switch.sh

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..252f78c79e
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch to a commit' '
+	test_must_fail git switch master^{commit}
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	git rev-parse master^ >expected &&
+	git rev-parse refs/heads/temp >actual &&
+	test_cmp expected actual &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	git switch -C temp &&
+	git rev-parse master >expected &&
+	git rev-parse refs/heads/temp >actual &&
+	test_cmp expected actual &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch' '
+	test_when_finished git switch master &&
+	git switch --orphan new-orphan master^ &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_commit expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch foo &&
+	git switch --guess foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_done
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 18/19] completion: support switch
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (16 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-30  9:48 ` [PATCH 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 499e56f83d..891abb72d7 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2126,6 +2126,32 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if ---guess was specified to enable DWIM mode
+		local track_opt= only_local_ref=n
+		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
+			track_opt='--track'
+		elif [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH 19/19] doc: promote "git switch"
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (17 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 18/19] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-01-30  9:48 ` Nguyễn Thái Ngọc Duy
  2019-01-31  7:39   ` Eric Sunshine
  2019-01-31 13:37 ` [PATCH 00/19] Add new command "switch" SZEDER Gábor
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-30  9:48 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-branch.txt           |  8 ++--
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 +++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 18 ++++-----
 Documentation/giteveryday.txt          | 24 ++++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 52 +++++++++++++-------------
 advice.c                               | 11 ++++--
 builtin/clone.c                        |  2 +-
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             |  4 +-
 18 files changed, 91 insertions(+), 86 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bf5316ffa9..94c1f24414 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -293,7 +293,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -319,7 +319,7 @@ NOTES
 -----
 
 If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
+easier to use the "git switch" command with its `-c` option to create
 a branch and check it out with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 27304428a1..3d42853529 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4dd5853d6e..420777c10b 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 9f69ae8b69..020ba838c9 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..6bd0f192cc 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git reset --hard master^2
-$ git checkout master
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..38015026bb 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch pu && git reset --hard next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..766e64d0e1 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
+The `git switch` command normally expects a branch head, but will also
 accept an arbitrary commit; for example, you can check out the commit
 referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index 567209aa79..835219b51b 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: checking out '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by performing another checkout.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"\n"
+	"  git switch -c <new-branch-name>\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/builtin/clone.c b/builtin/clone.c
index 7c7f98c72c..b06f0ed735 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -491,7 +491,7 @@ static enum {
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry the checkout with 'git switch -f HEAD'\n");
 
 static void remove_junk(void)
 {
diff --git a/sha1-name.c b/sha1-name.c
index a656481c6a..e5f0832995 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -745,7 +745,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..50f7d6c15c 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -204,7 +204,7 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 	If you want to create a new branch to retain commits you create, you may
 	do so (now or later) by using -b with the checkout command again. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit three
 	EOF
@@ -280,7 +280,7 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 	If you want to create a new branch to retain commits you create, you may
 	do so (now or later) by using -b with the checkout command again. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit... three
 	EOF
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-30  9:48 ` [PATCH 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
@ 2019-01-31  0:50   ` Eric Sunshine
  2019-01-31  5:29     ` Duy Nguyen
  2019-01-31 13:09   ` SZEDER Gábor
  1 sibling, 1 reply; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  0:50 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> "git checkout" doing too many things is a source of confusion for many
> users (and it even bites old timers sometimes). To remedy that, the
> command will be split into two new ones: switch and
> something-to-checkout-paths. The good old "git checkout" command is
> still here and will be until all (or most of users) are sick of it.
> [...]
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
> @@ -0,0 +1,236 @@
> +SYNOPSIS
> +'git switch' [<options>] [--guess] <branch>
> +'git switch' [<options>] --detach [<start_point>>]
> +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]

What does the third form mean when all optional arguments (that is,
_all_ arguments) are omitted?

Nit: I realize you inherited this from git-checkout documentation, but
can you use '-' rather than '_', as in <start-point>, <new-branch>, to
be consistent with other documentation, such as git-branch.

> +EXAMPLES
> +To check out commit "HEAD~3" for temporary inspection or experiment
> +without creating a new branch:
> +
> +------------
> +$ git switch --detach HEAD~3
> +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
> +------------
> +
> +If it turns out whatever you have done is worth keeping, you can
> +always create a new name for it (without switching away):
> +
> +------------
> +$ git switch -c good-surprises
> +------------

I had to go re-read git-branch documentation to understand why you
recommended "git switch -c <new-branch>" (or even why this mode
exists) over plain "git branch <new-branch>". I wonder if the
difference between the two deserves a bit more discussion here and
above in the description of the -c/-C options.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  0:50   ` Eric Sunshine
@ 2019-01-31  5:29     ` Duy Nguyen
  2019-01-31  6:24       ` Eric Sunshine
  2019-01-31 13:06       ` [PATCH 08/19] checkout: split part of it to new command switch SZEDER Gábor
  0 siblings, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-01-31  5:29 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List

On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > "git checkout" doing too many things is a source of confusion for many
> > users (and it even bites old timers sometimes). To remedy that, the
> > command will be split into two new ones: switch and
> > something-to-checkout-paths. The good old "git checkout" command is
> > still here and will be until all (or most of users) are sick of it.
> > [...]
> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> > ---
> > diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
> > @@ -0,0 +1,236 @@
> > +SYNOPSIS
> > +'git switch' [<options>] [--guess] <branch>
> > +'git switch' [<options>] --detach [<start_point>>]
> > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]
>
> What does the third form mean when all optional arguments (that is,
> _all_ arguments) are omitted?

"git switch" is smart (or too dumb to be clever):

$ git switch
fatal: nothing to do

> Nit: I realize you inherited this from git-checkout documentation, but
> can you use '-' rather than '_', as in <start-point>, <new-branch>, to
> be consistent with other documentation, such as git-branch.

A quick grep shows <some-thing> seems to win over <some_thing>. Yeah.

> > +EXAMPLES
> > +To check out commit "HEAD~3" for temporary inspection or experiment
> > +without creating a new branch:
> > +
> > +------------
> > +$ git switch --detach HEAD~3
> > +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
> > +------------
> > +
> > +If it turns out whatever you have done is worth keeping, you can
> > +always create a new name for it (without switching away):
> > +
> > +------------
> > +$ git switch -c good-surprises
> > +------------
>
> I had to go re-read git-branch documentation to understand why you
> recommended "git switch -c <new-branch>" (or even why this mode
> exists) over plain "git branch <new-branch>". I wonder if the
> difference between the two deserves a bit more discussion here and
> above in the description of the -c/-C options.

There is this bit in git-branch, will that help?

-- 8< --
NOTES
-----

If you are creating a branch that you want to checkout immediately, it is
easier to use the "git switch" command with its `-c` option to create
a branch and check it out with a single command.
-- 8< --

Otherwise any suggestions are welcome, I guess I could rephrase it a bit like

-c <new_branch>::
--create <new_branch>::
        Create a new branch named <new_branch> starting at
        <start_point> before switching to the branch. This is similar
        to creating a new branch with linkgit:git-branch[1] except
        that `git branch` does not switch branches.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  5:29     ` Duy Nguyen
@ 2019-01-31  6:24       ` Eric Sunshine
  2019-01-31  7:20         ` Eric Sunshine
  2019-01-31  7:43         ` Duy Nguyen
  2019-01-31 13:06       ` [PATCH 08/19] checkout: split part of it to new command switch SZEDER Gábor
  1 sibling, 2 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  6:24 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List

On Thu, Jan 31, 2019 at 12:29 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > > +'git switch' [<options>] [--guess] <branch>
> > > +'git switch' [<options>] --detach [<start_point>>]
> > > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]
> >
> > What does the third form mean when all optional arguments (that is,
> > _all_ arguments) are omitted?
>
> "git switch" is smart (or too dumb to be clever):
>
> $ git switch
> fatal: nothing to do

But does it need to be this way? Does it make a good user-experience?
I, personally, find it confusing to see that it can accept no
arguments. An alternative, perhaps more consistent with UX elsewhere:

    $ git switch
    fatal: missing branch argument

or something.

> > > +If it turns out whatever you have done is worth keeping, you can
> > > +always create a new name for it (without switching away):
> > > +------------
> > > +$ git switch -c good-surprises
> > > +------------
> >
> > I had to go re-read git-branch documentation to understand why you
> > recommended "git switch -c <new-branch>" (or even why this mode
> > exists) over plain "git branch <new-branch>". I wonder if the
> > difference between the two deserves a bit more discussion here and
> > above in the description of the -c/-C options.
>
> There is this bit in git-branch, will that help?
>
> -- 8< --
> If you are creating a branch that you want to checkout immediately, it is
> easier to use the "git switch" command with its `-c` option to create
> a branch and check it out with a single command.
> -- 8< --

That's pretty reasonable when reading git-branch documentation but
doesn't help when reading git-switch documentation.

(Also, shouldn't the bit about "want to checkout" be changed to "want
to switch to"?)

> Otherwise any suggestions are welcome, I guess I could rephrase it a bit like
>
> -c <new_branch>::
> --create <new_branch>::
>         Create a new branch named <new_branch> starting at
>         <start_point> before switching to the branch. This is similar
>         to creating a new branch with linkgit:git-branch[1] except
>         that `git branch` does not switch branches.

This is much better.

I find, however, that the top-level git-switch "DESCRIPTION" section,
which talks about "switching branches" doesn't actually ever explain
what it means to "switch" to a branch. Even adding a simple sentence
stating that "switching to a branch means that a newly-created commit
will be a direct child of the current head of the branch, and that the
branch will be updated to point at the new commit" would help cement
the meaning of branch switching in the reader's mind (rather than
assuming the reader understands that implicitly).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 09/19] switch: better names for -b and -B
  2019-01-30  9:48 ` [PATCH 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-01-31  7:03   ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  7:03 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> The shortcut of these options do not make much sense when used with
> switch. And their descriptions are also tied to checkout
> out. Move -b/-B to cmd_checkout() and new -c/-C with the same
> functionality in cmd_switch_branch()
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> @@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
> @@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>  int cmd_checkout(int argc, const char **argv, const char *prefix)
>  {
> +       struct option checkout_options[] = {
> +               OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
> +                          N_("create and checkout a new branch")),
> +               OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
> +                          N_("create/reset and checkout a branch")),

Note wording for -B and compare with...

> @@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
> +       struct option switch_options[] = {
> +               OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
> +                          N_("create and switch to a new branch")),
> +               OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
> +                          N_("create/reset and switch to a new branch")),

wording for -C which talks about "new branch" rather than just
"branch" of -B. (Dropping "new" from the latter makes it read a bit
better, in my opinion, and would make it consistent with -B wording.)

> +               OPT_END()
> +       };

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 17/19] t: add tests for switch
  2019-01-30  9:48 ` [PATCH 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-01-31  7:18   ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  7:18 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Wed, Jan 30, 2019 at 4:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
> @@ -0,0 +1,91 @@
> +test_expect_success 'switch and create branch' '
> +       test_when_finished git switch master &&
> +       git switch -c temp master^ &&
> +       git rev-parse master^ >expected &&
> +       git rev-parse refs/heads/temp >actual &&
> +       test_cmp expected actual &&

The above three lines could be replaced with test_cmp_rev()?

> +       echo refs/heads/temp >expected-branch &&
> +       git symbolic-ref HEAD >actual-branch &&
> +       test_cmp expected-branch actual-branch
> +'
> +
> +test_expect_success 'force create branch from HEAD' '
> +       test_when_finished git switch master &&
> +       git switch --detach master &&
> +       git switch -C temp &&
> +       git rev-parse master >expected &&
> +       git rev-parse refs/heads/temp >actual &&
> +       test_cmp expected actual &&

Ditto: test_cmp_rev()

> +       echo refs/heads/temp >expected-branch &&
> +       git symbolic-ref HEAD >actual-branch &&
> +       test_cmp expected-branch actual-branch
> +'

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  6:24       ` Eric Sunshine
@ 2019-01-31  7:20         ` Eric Sunshine
  2019-01-31  7:43         ` Duy Nguyen
  1 sibling, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  7:20 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List

On Thu, Jan 31, 2019 at 1:24 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Thu, Jan 31, 2019 at 12:29 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > > On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > > > +'git switch' [<options>] [--guess] <branch>
> > > > +'git switch' [<options>] --detach [<start_point>>]
> > > > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]
> > >
> > > What does the third form mean when all optional arguments (that is,
> > > _all_ arguments) are omitted?
> >
> > "git switch" is smart (or too dumb to be clever):
> >
> > $ git switch
> > fatal: nothing to do
>
> But does it need to be this way? Does it make a good user-experience?
> I, personally, find it confusing to see that it can accept no
> arguments. An alternative, perhaps more consistent with UX elsewhere:
>
>     $ git switch
>     fatal: missing branch argument
>
> or something.

Let me clarify by saying that I don't understand why the third form is
documented as validly accepting no arguments given that a no-argument
invocation is an error. That is, I would expect the third form of the
synopsis to say:

    'git switch' [<options>] (-c|-C|--orphan) <new_branch> [<start_point>]

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 19/19] doc: promote "git switch"
  2019-01-30  9:48 ` [PATCH 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-01-31  7:39   ` Eric Sunshine
  2019-01-31 11:28     ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  7:39 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Wed, Jan 30, 2019 at 4:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> The new command "git switch" is added to avoid the confusion of
> one-command-do-all "git checkout" for new users. They are also helpful
> to avoid ambiguation context.
>
> For these reasons, promote it everywhere possible. This includes
> documentation, suggestions/advice from other commands...
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
> @@ -319,7 +319,7 @@ NOTES
>  If you are creating a branch that you want to checkout immediately, it is

s/checkout/switch to/

> -easier to use the git checkout command with its `-b` option to create
> +easier to use the "git switch" command with its `-c` option to create
>  a branch and check it out with a single command.
> diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
> @@ -302,22 +302,22 @@ ref: refs/heads/master
>  Examining an old version without creating a new branch
>  ------------------------------------------------------
>
> -The `git checkout` command normally expects a branch head, but will also
> +The `git switch` command normally expects a branch head, but will also
>  accept an arbitrary commit; for example, you can check out the commit
>  referenced by a tag:

This may need to mention --detach explicitly. Perhaps:

    ...normally expects a branch head, but will also accept
    an arbitrary commit when invoked with --detach;...

> diff --git a/advice.c b/advice.c
> @@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)
>  void detach_advice(const char *new_name)
>  {
>         const char *fmt =
> -       _("Note: checking out '%s'.\n\n"
> +       _("Note: checking out '%s'.\n"

Do you also want to adjust this to avoid saying "checking out"?

> +       "\n"
>         "You are in 'detached HEAD' state. You can look around, make experimental\n"
>         "changes and commit them, and you can discard any commits you make in this\n"
> -       "state without impacting any branches by performing another checkout.\n\n"
> +       "state without impacting any branches by performing another checkout.\n"

Ditto: "checkout"?

> +       "\n"
>         "If you want to create a new branch to retain commits you create, you may\n"
> -       "do so (now or later) by using -b with the checkout command again. Example:\n\n"
> -       "  git checkout -b <new-branch-name>\n\n");
> +       "do so (now or later) by using -b with the checkout command again. Example:\n"

s/-b/-c/
s/checkout/switch/

> +       "\n"
> +       "  git switch -c <new-branch-name>\n\n");
> diff --git a/builtin/clone.c b/builtin/clone.c
> @@ -491,7 +491,7 @@ static enum {
>  static const char junk_leave_repo_msg[] =
>  N_("Clone succeeded, but checkout failed.\n"
>     "You can inspect what was checked out with 'git status'\n"
> -   "and retry the checkout with 'git checkout -f HEAD'\n");
> +   "and retry the checkout with 'git switch -f HEAD'\n");

Just wondering if these uses of "checkout" and "checked out" need
adjustment. I don't have any good suggestions, though.

> diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
> @@ -204,7 +204,7 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
>         If you want to create a new branch to retain commits you create, you may
>         do so (now or later) by using -b with the checkout command again. Example:

s/-b/-c/
s/checkout/switch/

> -         git checkout -b <new-branch-name>
> +         git switch -c <new-branch-name>
> @@ -280,7 +280,7 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
>         If you want to create a new branch to retain commits you create, you may
>         do so (now or later) by using -b with the checkout command again. Example:

s/-b/-c/
s/checkout/switch/

> -         git checkout -b <new-branch-name>
> +         git switch -c <new-branch-name>

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  6:24       ` Eric Sunshine
  2019-01-31  7:20         ` Eric Sunshine
@ 2019-01-31  7:43         ` Duy Nguyen
  2019-01-31  7:51           ` Eric Sunshine
  2019-01-31  9:21           ` [PATCH] git-commit.txt: better description what it does Nguyễn Thái Ngọc Duy
  1 sibling, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-01-31  7:43 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List

On Thu, Jan 31, 2019 at 1:25 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> I find, however, that the top-level git-switch "DESCRIPTION" section,
> which talks about "switching branches" doesn't actually ever explain
> what it means to "switch" to a branch. Even adding a simple sentence
> stating that "switching to a branch means that a newly-created commit
> will be a direct child of the current head of the branch, and that the
> branch will be updated to point at the new commit" would help cement
> the meaning of branch switching in the reader's mind (rather than
> assuming the reader understands that implicitly).

Thanks. How about this? I skipped the "update branch to point to the
new commit" because that sounds like something you should learn from
git-commit and hopefully the word "commit" would be enough to recall
that knowledge (or direct the user to git-commit.txt). I notice
git-commit.txt does not say anything about branch update business
though. Maybe some more updates there...

DESCRIPTION
-----------
Switch to a specified branch. The working three and the index are also
updated to match the branch. All new commits will be added to the tip
of this branch.

Optionally a new branch could be created with either `-c` or `-C`, or
detach the working tree from any branch with `--detach`, along with
switching.


-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  7:43         ` Duy Nguyen
@ 2019-01-31  7:51           ` Eric Sunshine
  2019-01-31  9:21           ` [PATCH] git-commit.txt: better description what it does Nguyễn Thái Ngọc Duy
  1 sibling, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31  7:51 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List

On Thu, Jan 31, 2019 at 2:44 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Thu, Jan 31, 2019 at 1:25 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > I find, however, that the top-level git-switch "DESCRIPTION" section,
> > which talks about "switching branches" doesn't actually ever explain
> > what it means to "switch" to a branch. Even adding a simple sentence
> > stating that "switching to a branch means that a newly-created commit
> > will be a direct child of the current head of the branch, and that the
> > branch will be updated to point at the new commit" would help cement
> > the meaning of branch switching in the reader's mind (rather than
> > assuming the reader understands that implicitly).
>
> Thanks. How about this? I skipped the "update branch to point to the
> new commit" ...

I came to the same conclusion (about dropping this bit) upon
re-reading what I wrote.

> DESCRIPTION
> -----------
> Switch to a specified branch. The working three and the index are also
> updated to match the branch. All new commits will be added to the tip
> of this branch.

Yes, this is good. Simple and direct. A couple minor fixes:

s/three/tree/
s/also//

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH] git-commit.txt: better description what it does
  2019-01-31  7:43         ` Duy Nguyen
  2019-01-31  7:51           ` Eric Sunshine
@ 2019-01-31  9:21           ` Nguyễn Thái Ngọc Duy
  2019-01-31 10:49             ` Eric Sunshine
  2019-02-01 10:09             ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  1 sibling, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-01-31  9:21 UTC (permalink / raw)
  To: git; +Cc: Eric Sunshine, Junio C Hamano, Nguyễn Thái Ngọc Duy

The description of git-commit jumps right into the commit content, which
is important, but it fails to mention how the commit is "added" to the
repository. Update the first paragraph saying a bit more about branch
update to fill this gap.

While at there, add a couple linkgit references when the command is
first mentioned.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 On Thu, Jan 31, 2019 at 02:43:41PM +0700, Duy Nguyen wrote:
 > I notice git-commit.txt does not say anything about branch update
 > business though. Maybe some more updates there...

 Maybe something like this. Although I feel I'm quite close to some
 rabbit hole.

Documentation/git-commit.txt | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index f970a43422..8148ba10b3 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -17,16 +17,19 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Stores the current contents of the index in a new commit along
-with a log message from the user describing the changes.
+Creates a new commit containing the current contents of the index with
+a log message from the user describing the changes. The commit is the
+direct child of the tip of the current branch. The branch is updated
+to point to the new commit (unless no branch is associated with the
+working tree, see "NOTES" section).
 
-The content to be added can be specified in several ways:
+The content to be committed can be specified in several ways:
 
-1. by using 'git add' to incrementally "add" changes to the
-   index before using the 'commit' command (Note: even modified
-   files must be "added");
+1. by using linkgit:git-add[1] to incrementally "add" changes to the
+   index before using the 'commit' command (Note: even modified files
+   must be "added");
 
-2. by using 'git rm' to remove files from the working tree
+2. by using linkgit:git-rm[1] to remove files from the working tree
    and the index, again before using the 'commit' command;
 
 3. by listing files as arguments to the 'commit' command
@@ -349,6 +352,18 @@ changes to tracked files.
 :git-commit: 1
 include::date-formats.txt[]
 
+NOTES
+-----
+If a branch is associated with the working tree, 'HEAD' points to this
+branch. When a new commit is created, the branch is updated to point
+to the new commit. As a result, resolving 'HEAD' still gives the new
+commit.
+
+If no branch is associated with the working tree (i.e. "detached HEAD"
+as described in linkgit:git-checkout[1]), 'HEAD' records the object
+name of the previous commit directly. When a new commit is created, it
+will be updated to point to the new commit.
+
 EXAMPLES
 --------
 When recording your own work, the contents of modified files in
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH] git-commit.txt: better description what it does
  2019-01-31  9:21           ` [PATCH] git-commit.txt: better description what it does Nguyễn Thái Ngọc Duy
@ 2019-01-31 10:49             ` Eric Sunshine
  2019-01-31 11:04               ` Duy Nguyen
  2019-02-01 10:09             ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  1 sibling, 1 reply; 289+ messages in thread
From: Eric Sunshine @ 2019-01-31 10:49 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano

On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> The description of git-commit jumps right into the commit content, which
> is important, but it fails to mention how the commit is "added" to the
> repository. Update the first paragraph saying a bit more about branch
> update to fill this gap.
>
> While at there, add a couple linkgit references when the command is
> first mentioned.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> @@ -17,16 +17,19 @@ SYNOPSIS
>  DESCRIPTION
>  -----------
> +Creates a new commit containing the current contents of the index with
> +a log message from the user describing the changes. The commit is the
> +direct child of the tip of the current branch. The branch is updated
> +to point to the new commit (unless no branch is associated with the
> +working tree, see "NOTES" section).

Okay. The information about the branch being updated makes sense.

> +NOTES
> +-----
> +If a branch is associated with the working tree, 'HEAD' points to this
> +branch. When a new commit is created, the branch is updated to point
> +to the new commit. As a result, resolving 'HEAD' still gives the new
> +commit.

I'm not sure I understand the purpose of the final sentence about HEAD
"still resolving" when you were, just before that, talking about the
branch.

> +If no branch is associated with the working tree (i.e. "detached HEAD"
> +as described in linkgit:git-checkout[1]), 'HEAD' records the object
> +name of the previous commit directly. When a new commit is created, it
> +will be updated to point to the new commit.

I'm having a hard time figuring out what these two paragraphs together
want to say. I _think_ they want to say that HEAD is updated
automatically to point at the latest commit, and that if a branch
points at HEAD, then the branch is is updated along with HEAD,
otherwise if no branch, then it's a "detached HEAD".

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] git-commit.txt: better description what it does
  2019-01-31 10:49             ` Eric Sunshine
@ 2019-01-31 11:04               ` Duy Nguyen
  2019-02-01  0:21                 ` Eric Sunshine
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-01-31 11:04 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List, Junio C Hamano

On Thu, Jan 31, 2019 at 5:49 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > The description of git-commit jumps right into the commit content, which
> > is important, but it fails to mention how the commit is "added" to the
> > repository. Update the first paragraph saying a bit more about branch
> > update to fill this gap.
> >
> > While at there, add a couple linkgit references when the command is
> > first mentioned.
> >
> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> > ---
> > diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> > @@ -17,16 +17,19 @@ SYNOPSIS
> >  DESCRIPTION
> >  -----------
> > +Creates a new commit containing the current contents of the index with
> > +a log message from the user describing the changes. The commit is the
> > +direct child of the tip of the current branch. The branch is updated
> > +to point to the new commit (unless no branch is associated with the
> > +working tree, see "NOTES" section).
>
> Okay. The information about the branch being updated makes sense.
>
> > +NOTES
> > +-----
> > +If a branch is associated with the working tree, 'HEAD' points to this
> > +branch. When a new commit is created, the branch is updated to point
> > +to the new commit. As a result, resolving 'HEAD' still gives the new
> > +commit.
>
> I'm not sure I understand the purpose of the final sentence about HEAD
> "still resolving" when you were, just before that, talking about the
> branch.
>
> > +If no branch is associated with the working tree (i.e. "detached HEAD"
> > +as described in linkgit:git-checkout[1]), 'HEAD' records the object
> > +name of the previous commit directly. When a new commit is created, it
> > +will be updated to point to the new commit.
>
> I'm having a hard time figuring out what these two paragraphs together
> want to say. I _think_ they want to say that HEAD is updated
> automatically to point at the latest commit, and that if a branch
> points at HEAD, then the branch is is updated along with HEAD,
> otherwise if no branch, then it's a "detached HEAD".

I felt some more explanation was needed when I mentioned about
updating current branch in the description, but then what happens when
there's no branch (aka detached HEAD)? There is still some updates. If
you consider HEAD a branch, then it's ok, but otherwise something is
missing. But perhaps this is the kind of details that could be left
out?
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 19/19] doc: promote "git switch"
  2019-01-31  7:39   ` Eric Sunshine
@ 2019-01-31 11:28     ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-01-31 11:28 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List

On Thu, Jan 31, 2019 at 2:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > diff --git a/builtin/clone.c b/builtin/clone.c
> > @@ -491,7 +491,7 @@ static enum {
> >  static const char junk_leave_repo_msg[] =
> >  N_("Clone succeeded, but checkout failed.\n"
> >     "You can inspect what was checked out with 'git status'\n"
> > -   "and retry the checkout with 'git checkout -f HEAD'\n");
> > +   "and retry the checkout with 'git switch -f HEAD'\n");
>
> Just wondering if these uses of "checkout" and "checked out" need
> adjustment. I don't have any good suggestions, though.

I think I'm going to leave this message alone for now. Technically
"git switch" can replace "git checkout" here but I feel this is more
about restoring files on worktree than switching branches. So when
"git restore" comes, I'll change it to say "git restore --from=HEAD
:/" or something like that instead.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31  5:29     ` Duy Nguyen
  2019-01-31  6:24       ` Eric Sunshine
@ 2019-01-31 13:06       ` SZEDER Gábor
  1 sibling, 0 replies; 289+ messages in thread
From: SZEDER Gábor @ 2019-01-31 13:06 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Eric Sunshine, Git List

On Thu, Jan 31, 2019 at 12:29:01PM +0700, Duy Nguyen wrote:
> > > +EXAMPLES
> > > +To check out commit "HEAD~3" for temporary inspection or experiment
> > > +without creating a new branch:
> > > +
> > > +------------
> > > +$ git switch --detach HEAD~3
> > > +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
> > > +------------
> > > +
> > > +If it turns out whatever you have done is worth keeping, you can
> > > +always create a new name for it (without switching away):
> > > +
> > > +------------
> > > +$ git switch -c good-surprises
> > > +------------
> >
> > I had to go re-read git-branch documentation to understand why you
> > recommended "git switch -c <new-branch>" (or even why this mode
> > exists) over plain "git branch <new-branch>". I wonder if the
> > difference between the two deserves a bit more discussion here and
> > above in the description of the -c/-C options.
> 
> There is this bit in git-branch, will that help?
> 
> -- 8< --
> NOTES
> -----
> 
> If you are creating a branch that you want to checkout immediately, it is
> easier to use the "git switch" command with its `-c` option to create
> a branch and check it out with a single command.
> -- 8< --
> 
> Otherwise any suggestions are welcome, I guess I could rephrase it a bit like
> 
> -c <new_branch>::
> --create <new_branch>::
>         Create a new branch named <new_branch> starting at
>         <start_point> before switching to the branch. This is similar
>         to creating a new branch with linkgit:git-branch[1] except
>         that `git branch` does not switch branches.

I think the second sentence, in particular the "except..." part, in
this description goes backwards by explaining how 'git branch' differs
from this.  I think the most to-the-point explanation instead of that
second sentence would be something like this:

  This is a shorthand for 'git branch <new-branch> [<start-point>] &&
  git switch <new-branch>'.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-30  9:48 ` [PATCH 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
  2019-01-31  0:50   ` Eric Sunshine
@ 2019-01-31 13:09   ` SZEDER Gábor
  2019-02-08  8:46     ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: SZEDER Gábor @ 2019-01-31 13:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

On Wed, Jan 30, 2019 at 04:48:20PM +0700, Nguyễn Thái Ngọc Duy wrote:
> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
> new file mode 100644
> index 0000000000..953c9747b2

> +--orphan <new_branch>::
> +	Create a new 'orphan' branch, named <new_branch>, started from
> +	<start_point> and switch to it. See explanation of the same
> +	option in linkgit:git-checkout[1] for details.

Copy-paste error?  An orphan branch, by definition, doesn't have a
starting point.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 00/19] Add new command "switch"
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (18 preceding siblings ...)
  2019-01-30  9:48 ` [PATCH 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-01-31 13:37 ` SZEDER Gábor
  2019-01-31 14:51   ` Duy Nguyen
  2019-01-31 18:13 ` Junio C Hamano
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
  21 siblings, 1 reply; 289+ messages in thread
From: SZEDER Gábor @ 2019-01-31 13:37 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Eric Sunshine

On Wed, Jan 30, 2019 at 04:48:12PM +0700, Nguyễn Thái Ngọc Duy wrote:
> The
> only one left is adding options similar to "git reset" (but with
> better names this time than --soft and --hard, hopefully).

Oh no, what for?!  As the commit message or patch 19/19 'doc: promote
"git switch"' states:

  The new command "git switch" is added to avoid the confusion of
  one-command-do-all "git checkout" for new users.

Adding '--soft|--hard' from 'git reset' would go in the opposite
direction.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 00/19] Add new command "switch"
  2019-01-31 13:37 ` [PATCH 00/19] Add new command "switch" SZEDER Gábor
@ 2019-01-31 14:51   ` Duy Nguyen
  2019-01-31 18:23     ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-01-31 14:51 UTC (permalink / raw)
  To: SZEDER Gábor; +Cc: Git Mailing List, Eric Sunshine

On Thu, Jan 31, 2019 at 8:37 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
>
> On Wed, Jan 30, 2019 at 04:48:12PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > The
> > only one left is adding options similar to "git reset" (but with
> > better names this time than --soft and --hard, hopefully).
>
> Oh no, what for?!  As the commit message or patch 19/19 'doc: promote
> "git switch"' states:
>
>   The new command "git switch" is added to avoid the confusion of
>   one-command-do-all "git checkout" for new users.
>
> Adding '--soft|--hard' from 'git reset' would go in the opposite
> direction.

If it's about the confusion, I think we can avoid it. If it's about
the one-command-do-all, I think it still fits in the main topic of
git-switch, which is about switching. But in git-reset case it's
switching HEAD, not a normal branch.

What I have in mind so far is this, let's see if it's so bad so I can
find another direction.

git switch --reset-branch [--keep-index] [--keep-worktree] [<commit>]

This updates worktree and index to <commit> and "rewinds" the current
branch to <commit>. If either of those --keep-* is given, that part
will be left untouched. So "git reset --hard" is the same as "git
switch --reset-branch", --soft "--reset-branch --keep-index
--keep-worktree" and --mixed "--reset-branch --keep-worktree".

I've been updating docs with this new format to see what it looks like
and I think it expresses the idea behind git-reset much better.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 00/19] Add new command "switch"
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (19 preceding siblings ...)
  2019-01-31 13:37 ` [PATCH 00/19] Add new command "switch" SZEDER Gábor
@ 2019-01-31 18:13 ` Junio C Hamano
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
  21 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-01-31 18:13 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This is the first half of the previous "switch-branch and
> restore-files" RFC series [1]. I only focus on switch-branch for now
> because it's already getting long and I still have some work to do on
> restore-files.

Exciting ;-)  This will probably become the best UI improvement in
the past few years.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 00/19] Add new command "switch"
  2019-01-31 14:51   ` Duy Nguyen
@ 2019-01-31 18:23     ` Junio C Hamano
  2019-02-02 11:06       ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-01-31 18:23 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: SZEDER Gábor, Git Mailing List, Eric Sunshine

Duy Nguyen <pclouds@gmail.com> writes:

>> Adding '--soft|--hard' from 'git reset' would go in the opposite
>> direction.
>
> If it's about the confusion, I think we can avoid it. If it's about
> the one-command-do-all, I think it still fits in the main topic of
> git-switch, which is about switching. But in git-reset case it's
> switching HEAD, not a normal branch.

I do not think "switch branch" should update HEAD for the branch.
Let it be handled by "reset" (or "branch -f that-other-branch").

I personally did not have "it is way too overloaded" problem with
"checkout", but it turns out that many others found it so.  You'll
see the same happen for your "switch" if you do not resist
temptation to add unrelated things (or things you may find related
but you see others find unrelated even in this early discussion).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] git-commit.txt: better description what it does
  2019-01-31 11:04               ` Duy Nguyen
@ 2019-02-01  0:21                 ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-02-01  0:21 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List, Junio C Hamano

On Thu, Jan 31, 2019 at 6:04 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Thu, Jan 31, 2019 at 5:49 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> > > +NOTES
> > > +-----
> > > +If a branch is associated with the working tree, 'HEAD' points to this
> > > +branch. When a new commit is created, the branch is updated to point
> > > +to the new commit. As a result, resolving 'HEAD' still gives the new
> > > +commit.
> > > +
> > > +If no branch is associated with the working tree (i.e. "detached HEAD"
> > > +as described in linkgit:git-checkout[1]), 'HEAD' records the object
> > > +name of the previous commit directly. When a new commit is created, it
> > > +will be updated to point to the new commit.
> >
> > I'm having a hard time figuring out what these two paragraphs together
> > want to say. I _think_ they want to say that HEAD is updated
> > automatically to point at the latest commit, and that if a branch
> > points at HEAD, then the branch is is updated along with HEAD,
> > otherwise if no branch, then it's a "detached HEAD".
>
> I felt some more explanation was needed when I mentioned about
> updating current branch in the description, but then what happens when
> there's no branch (aka detached HEAD)? There is still some updates. If
> you consider HEAD a branch, then it's ok, but otherwise something is
> missing. But perhaps this is the kind of details that could be left
> out?

It could be left out or incorporated directly into the DESCRIPTION
section with a link to the much more thorough discussion of detached
HEAD in git-checkout documentation. Here's my attempt at rewriting
DESCRIPTION (and dropping NOTES altogether):

    Create a new commit containing the current contents of the index
    and the given log message describing the changes. The new commit
    is a direct child of HEAD, usually the tip of the current branch,
    and the branch is updated to point to it (unless no branch is
    associated with the working tree, in which case HEAD is "detached"
    as described in linkgit:git-checkout[1]).

(I also streamlined the above a bit from what you had composed.)

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v2] git-commit.txt: better description what it does
  2019-01-31  9:21           ` [PATCH] git-commit.txt: better description what it does Nguyễn Thái Ngọc Duy
  2019-01-31 10:49             ` Eric Sunshine
@ 2019-02-01 10:09             ` Nguyễn Thái Ngọc Duy
  2019-02-04 17:59               ` Junio C Hamano
  1 sibling, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-01 10:09 UTC (permalink / raw)
  To: git; +Cc: Eric Sunshine, Junio C Hamano, Nguyễn Thái Ngọc Duy

The description of git-commit jumps right into the commit content, which
is important, but it fails to mention how the commit is "added" to the
repository. Update the first paragraph saying a bit more about branch
update to fill this gap.

While at there, add a couple linkgit references when the command is
first mentioned.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 NOTES section is gone. First paragraph updated.

 Documentation/git-commit.txt | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index f970a43422..a85c2c2a4c 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -17,16 +17,20 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Stores the current contents of the index in a new commit along
-with a log message from the user describing the changes.
+Create a new commit containing the current contents of the index and
+the given log message describing the changes. The new commit is a
+direct child of HEAD, usually the tip of the current branch, and the
+branch is updated to point to it (unless no branch is associated with
+the working tree, in which case HEAD is "detached" as described in
+linkgit:git-checkout[1]).
 
-The content to be added can be specified in several ways:
+The content to be committed can be specified in several ways:
 
-1. by using 'git add' to incrementally "add" changes to the
-   index before using the 'commit' command (Note: even modified
-   files must be "added");
+1. by using linkgit:git-add[1] to incrementally "add" changes to the
+   index before using the 'commit' command (Note: even modified files
+   must be "added");
 
-2. by using 'git rm' to remove files from the working tree
+2. by using linkgit:git-rm[1] to remove files from the working tree
    and the index, again before using the 'commit' command;
 
 3. by listing files as arguments to the 'commit' command
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH 00/19] Add new command "switch"
  2019-01-31 18:23     ` Junio C Hamano
@ 2019-02-02 11:06       ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-02-02 11:06 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: SZEDER Gábor, Git Mailing List, Eric Sunshine

On Fri, Feb 1, 2019 at 1:23 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> >> Adding '--soft|--hard' from 'git reset' would go in the opposite
> >> direction.
> >
> > If it's about the confusion, I think we can avoid it. If it's about
> > the one-command-do-all, I think it still fits in the main topic of
> > git-switch, which is about switching. But in git-reset case it's
> > switching HEAD, not a normal branch.
>
> I do not think "switch branch" should update HEAD for the branch.
> Let it be handled by "reset" (or "branch -f that-other-branch").

It can already, it's simply a shortcut for "git switch --force-create
<current-branch> -f <commit>" (or "git checkout -fB <current-branch>
<commit>"), at least for --hard. --mixed and --soft is just a
different variant of "-f".

> I personally did not have "it is way too overloaded" problem with
> "checkout", but it turns out that many others found it so.  You'll
> see the same happen for your "switch" if you do not resist
> temptation to add unrelated things (or things you may find related
> but you see others find unrelated even in this early discussion).

The thing about git-reset is it's also overloaded with updating things
other than HEAD. All the pathspec form is the same as checkout/restore
(except checkout updates both index/worktree while reset is more about
index; but restore can cover all index/worktree combination). But yeah
maybe just leave it for now. While trying to document the new option
in switch to replace reset, the verb "rewind" (the tip of the current
branch) seems fitting well. I might revisit this topic with git-rewind
or something.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2] git-commit.txt: better description what it does
  2019-02-01 10:09             ` [PATCH v2] " Nguyễn Thái Ngọc Duy
@ 2019-02-04 17:59               ` Junio C Hamano
  0 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-02-04 17:59 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Eric Sunshine

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> The description of git-commit jumps right into the commit content, which
> is important, but it fails to mention how the commit is "added" to the
> repository. Update the first paragraph saying a bit more about branch
> update to fill this gap.
>
> While at there, add a couple linkgit references when the command is
> first mentioned.
>
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  NOTES section is gone. First paragraph updated.

I haven't been following the discussion in the previous round, but
this looks like a sensible change.  Thanks, both.


>
>  Documentation/git-commit.txt | 18 +++++++++++-------
>  1 file changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> index f970a43422..a85c2c2a4c 100644
> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -17,16 +17,20 @@ SYNOPSIS
>  
>  DESCRIPTION
>  -----------
> -Stores the current contents of the index in a new commit along
> -with a log message from the user describing the changes.
> +Create a new commit containing the current contents of the index and
> +the given log message describing the changes. The new commit is a
> +direct child of HEAD, usually the tip of the current branch, and the
> +branch is updated to point to it (unless no branch is associated with
> +the working tree, in which case HEAD is "detached" as described in
> +linkgit:git-checkout[1]).
>  
> -The content to be added can be specified in several ways:
> +The content to be committed can be specified in several ways:
>  
> -1. by using 'git add' to incrementally "add" changes to the
> -   index before using the 'commit' command (Note: even modified
> -   files must be "added");
> +1. by using linkgit:git-add[1] to incrementally "add" changes to the
> +   index before using the 'commit' command (Note: even modified files
> +   must be "added");
>  
> -2. by using 'git rm' to remove files from the working tree
> +2. by using linkgit:git-rm[1] to remove files from the working tree
>     and the index, again before using the 'commit' command;
>  
>  3. by listing files as arguments to the 'commit' command

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH 08/19] checkout: split part of it to new command switch
  2019-01-31 13:09   ` SZEDER Gábor
@ 2019-02-08  8:46     ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-02-08  8:46 UTC (permalink / raw)
  To: SZEDER Gábor; +Cc: Git Mailing List

On Thu, Jan 31, 2019 at 8:09 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
>
> On Wed, Jan 30, 2019 at 04:48:20PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
> > new file mode 100644
> > index 0000000000..953c9747b2
>
> > +--orphan <new_branch>::
> > +     Create a new 'orphan' branch, named <new_branch>, started from
> > +     <start_point> and switch to it. See explanation of the same
> > +     option in linkgit:git-checkout[1] for details.
>
> Copy-paste error?  An orphan branch, by definition, doesn't have a
> starting point.

It does not have any parents, true. The "started from <start-point>"
is not super clear on this, but git-checkout.txt describes more and
says that it's essentially "git checkout <start_point>" then make an
orphan branch based on HEAD^{tree}.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v2 00/19] Add new command "switch"
  2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
                   ` (20 preceding siblings ...)
  2019-01-31 18:13 ` Junio C Hamano
@ 2019-02-08  9:03 ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                     ` (19 more replies)
  21 siblings, 20 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

This series adds a new command "switch" for switching branches, an
alternative for "git checkout".

v2 fixes the comments I got from v1. I think the range-diff below
summarizes it better than I do. No major changes though.

Range-diff dựa trên v1:
 1:  6d550b443c !  1:  2283204090 checkout: split part of it to new command switch
    @@ -148,24 +148,33 @@
     +--------
     +[verse]
     +'git switch' [<options>] [--guess] <branch>
    -+'git switch' [<options>] --detach [<start_point>>]
    -+'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]
    ++'git switch' [<options>] --detach [<start-point>]
    ++'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
     +
     +DESCRIPTION
     +-----------
    -+Switch to a specified branch and update files in the working tree to
    -+match it. Optionally a new branch could be created with either `-c` or
    -+`-C`, or detach from any branch with `--detach`, along with switching.
    ++Switch to a specified branch. The working tree and the index are
    ++updated to match the branch. All new commits will be added to the tip
    ++of this branch.
    ++
    ++Optionally a new branch could be created with either `-c` or `-C`, or
    ++detach the working tree from any branch with `--detach`, along with
    ++switching.
    ++
    ++Switching branches does not require a clean index and working tree
    ++(i.e. no differences compared to 'HEAD'). The operation is aborted
    ++however if the switch leads to loss of local changes, unless told
    ++otherwise.
     +
     +OPTIONS
     +-------
     +<branch>::
     +	Branch to switch to.
     +
    -+<new_branch>::
    ++<new-branch>::
     +	Name for the new branch.
     +
    -+<start_point>::
    ++<start-point>::
     +	The name of a commit at which to switch to before creating a
     +	new branch or detach from.
     ++
    @@ -177,17 +186,27 @@
     +base of `A` and `B` if there is exactly one merge base. You can leave
     +out at most one of `A` and `B`, in which case it defaults to `HEAD`.
     +
    -+-c <new_branch>::
    -+--create <new_branch>::
    -+	Create a new branch named <new_branch> and start it at
    -+	<start_point>; see linkgit:git-branch[1] for details.
    ++-c <new-branch>::
    ++--create <new-branch>::
    ++	Create a new branch named <new-branch> starting at
    ++	<start-point> before switching to the branch. This is a
    ++	convenient shortcut for:
    +++
    ++------------
    ++$ git branch <new-branch>
    ++$ git switch <new-branch>
    ++------------
     +
    -+-C <new_branch>::
    -+--force-create <new_branch>::
    -+	Creates the branch <new_branch> and start it at <start_point>;
    -+	if it already exists, then reset it to <start_point>. This is
    -+	equivalent to running "git branch" with "-f"; see
    -+	linkgit:git-branch[1] for details.
    ++-C <new-branch>::
    ++--force-create <new-branch>::
    ++	Similar to `--create` except that if <new-branch> already
    ++	exists, it will be reset to <start-point>. This is a
    ++	convenient shortcut for:
    +++
    ++------------
    ++$ git branch -f <new-branch>
    ++$ git switch <new-branch>
    ++------------
     +
     +-d::
     +--detach::
    @@ -220,7 +239,9 @@
     +-f::
     +--force::
     +	Proceed even if the index or the working tree differs from
    -+	HEAD.  This is used to throw away local changes.
    ++	HEAD. Both the index and working tree are restored to match
    ++	the switching target. This is used to throw away local
    ++	changes.
     +
     +--[no-]progress::
     +	Progress status is reported on the standard error stream
    @@ -270,9 +291,9 @@
     +	"merge" (default) and "diff3" (in addition to what is shown by
     +	"merge" style, shows the original contents).
     +
    -+--orphan <new_branch>::
    -+	Create a new 'orphan' branch, named <new_branch>, started from
    -+	<start_point> and switch to it. See explanation of the same
    ++--orphan <new-branch>::
    ++	Create a new 'orphan' branch, named <new-branch>, started from
    ++	<start-point> and switch to it. See explanation of the same
     +	option in linkgit:git-checkout[1] for details.
     +
     +--ignore-other-worktrees::
 2:  e2f94df471 !  2:  deceb221dc switch: better names for -b and -B
    @@ -61,7 +61,7 @@
     +		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
     +			   N_("create and switch to a new branch")),
     +		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
    -+			   N_("create/reset and switch to a new branch")),
    ++			   N_("create/reset and switch to a branch")),
     +		OPT_END()
     +	};
      	int ret;
 3:  b3d431a97f =  3:  08f28e9726 switch: remove -l
 4:  26738818fa =  4:  9ee4852c8b switch: stop accepting pathspec
 5:  79ebe7a68c !  5:  0a7ea4b33b switch: reject "do nothing" case
    @@ -32,7 +32,7 @@
     +	    !new_branch_info->name &&
     +	    !opts->new_branch &&
     +	    !opts->force_detach)
    -+		die(_("nothing to do"));
    ++		die(_("missing branch or commit argument"));
     +
      	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
      	    !opts->ignore_other_worktrees) {
 6:  bc36ac8510 !  6:  eedf82c72b switch: only allow explicit detached HEAD
    @@ -26,7 +26,7 @@
      	int ignore_skipworktree;
     @@
      	    !opts->force_detach)
    - 		die(_("nothing to do"));
    + 		die(_("missing branch or commit argument"));
      
     +	if (!opts->implicit_detach &&
     +	    !opts->force_detach &&
 7:  1a761ce8e5 =  7:  9314ff16c9 switch: add short option for --detach
 8:  738c560eb9 !  8:  9d8e461df2 switch: no implicit dwim, use --guess to dwim
    @@ -143,7 +143,7 @@
     @@
      			   N_("create and switch to a new branch")),
      		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
    - 			   N_("create/reset and switch to a new branch")),
    + 			   N_("create/reset and switch to a branch")),
     +		OPT_BOOL('g', "guess", &opts.dwim_new_local_branch,
     +			 N_("second guess 'git switch <no-such-branch>'")),
      		OPT_END()
 9:  456e9340f1 =  9:  749665c457 switch: no worktree status unless real branch switch happens
10:  cdd132a7e2 ! 10:  e4e0b397eb t: add tests for switch
    @@ -51,9 +51,7 @@
     +test_expect_success 'switch and create branch' '
     +	test_when_finished git switch master &&
     +	git switch -c temp master^ &&
    -+	git rev-parse master^ >expected &&
    -+	git rev-parse refs/heads/temp >actual &&
    -+	test_cmp expected actual &&
    ++	test_cmp_rev master^ refs/heads/temp &&
     +	echo refs/heads/temp >expected-branch &&
     +	git symbolic-ref HEAD >actual-branch &&
     +	test_cmp expected-branch actual-branch
    @@ -63,9 +61,7 @@
     +	test_when_finished git switch master &&
     +	git switch --detach master &&
     +	git switch -C temp &&
    -+	git rev-parse master >expected &&
    -+	git rev-parse refs/heads/temp >actual &&
    -+	test_cmp expected actual &&
    ++	test_cmp_rev master refs/heads/temp &&
     +	echo refs/heads/temp >expected-branch &&
     +	git symbolic-ref HEAD >actual-branch &&
     +	test_cmp expected-branch actual-branch
11:  96a97fcab4 = 11:  b9a7662271 completion: support switch
12:  8d984bc684 ! 12:  7449a0c4cc doc: promote "git switch"
    @@ -40,14 +40,18 @@
      +
      <1> This step and the next one could be combined into a single step with
     @@
    + NOTES
      -----
      
    - If you are creating a branch that you want to checkout immediately, it is
    +-If you are creating a branch that you want to checkout immediately, it is
     -easier to use the git checkout command with its `-b` option to create
    -+easier to use the "git switch" command with its `-c` option to create
    - a branch and check it out with a single command.
    +-a branch and check it out with a single command.
    ++If you are creating a branch that you want to switch to immediately,
    ++it is easier to use the "git switch" command with its `-c` option to
    ++do the same thing with a single command.
      
      The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
    + serve four related but different purposes:
     
      diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
      --- a/Documentation/git-check-ref-format.txt
    @@ -473,9 +477,11 @@
      ------------------------------------------------------
      
     -The `git checkout` command normally expects a branch head, but will also
    +-accept an arbitrary commit; for example, you can check out the commit
    +-referenced by a tag:
     +The `git switch` command normally expects a branch head, but will also
    - accept an arbitrary commit; for example, you can check out the commit
    - referenced by a tag:
    ++accept an arbitrary commit when invoked with --detach; for example,
    ++you can check out the commit referenced by a tag:
      
      ------------------------------------------------
     -$ git checkout v2.6.17
    @@ -628,36 +634,23 @@
      {
      	const char *fmt =
     -	_("Note: checking out '%s'.\n\n"
    -+	_("Note: checking out '%s'.\n"
    ++	_("Note: switching to '%s'.\n"
     +	"\n"
      	"You are in 'detached HEAD' state. You can look around, make experimental\n"
      	"changes and commit them, and you can discard any commits you make in this\n"
     -	"state without impacting any branches by performing another checkout.\n\n"
    -+	"state without impacting any branches by performing another checkout.\n"
    ++	"state without impacting any branches by switching back to a branch.\n"
     +	"\n"
      	"If you want to create a new branch to retain commits you create, you may\n"
     -	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
     -	"  git checkout -b <new-branch-name>\n\n");
    -+	"do so (now or later) by using -b with the checkout command again. Example:\n"
    ++	"do so (now or later) by using -c with the switch command. Example:\n"
     +	"\n"
     +	"  git switch -c <new-branch-name>\n\n");
      
      	fprintf(stderr, fmt, new_name);
      }
     
    - diff --git a/builtin/clone.c b/builtin/clone.c
    - --- a/builtin/clone.c
    - +++ b/builtin/clone.c
    -@@
    - static const char junk_leave_repo_msg[] =
    - N_("Clone succeeded, but checkout failed.\n"
    -    "You can inspect what was checked out with 'git status'\n"
    --   "and retry the checkout with 'git checkout -f HEAD'\n");
    -+   "and retry the checkout with 'git switch -f HEAD'\n");
    - 
    - static void remove_junk(void)
    - {
    -
      diff --git a/sha1-name.c b/sha1-name.c
      --- a/sha1-name.c
      +++ b/sha1-name.c
    @@ -675,8 +668,20 @@
      --- a/t/t2020-checkout-detach.sh
      +++ b/t/t2020-checkout-detach.sh
     @@
    + 
    + 	# The first detach operation is more chatty than the following ones.
    + 	cat >1st_detach <<-EOF &&
    +-	Note: checking out 'HEAD^'.
    ++	Note: switching to 'HEAD^'.
    + 
    + 	You are in 'detached HEAD' state. You can look around, make experimental
    + 	changes and commit them, and you can discard any commits you make in this
    +-	state without impacting any branches by performing another checkout.
    ++	state without impacting any branches by switching back to a branch.
    + 
      	If you want to create a new branch to retain commits you create, you may
    - 	do so (now or later) by using -b with the checkout command again. Example:
    +-	do so (now or later) by using -b with the checkout command again. Example:
    ++	do so (now or later) by using -c with the switch command. Example:
      
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
    @@ -684,8 +689,20 @@
      	HEAD is now at \$commit three
      	EOF
     @@
    + 
    + 	# The first detach operation is more chatty than the following ones.
    + 	cat >1st_detach <<-EOF &&
    +-	Note: checking out 'HEAD^'.
    ++	Note: switching to 'HEAD^'.
    + 
    + 	You are in 'detached HEAD' state. You can look around, make experimental
    + 	changes and commit them, and you can discard any commits you make in this
    +-	state without impacting any branches by performing another checkout.
    ++	state without impacting any branches by switching back to a branch.
    + 
      	If you want to create a new branch to retain commits you create, you may
    - 	do so (now or later) by using -b with the checkout command again. Example:
    +-	do so (now or later) by using -b with the checkout command again. Example:
    ++	do so (now or later) by using -c with the switch command. Example:
      
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
13:  91e5442069 <  -:  ---------- checkout: split part of it to new command restore
14:  6bc8f87c0a <  -:  ---------- restore: take tree-ish from --from option instead
15:  095a1cd3e3 <  -:  ---------- restore: make pathspec mandatory
16:  6eb5612187 <  -:  ---------- restore: disable overlay mode by default
17:  cc79522047 <  -:  ---------- t: add tests for restore
18:  ed5d7097b6 <  -:  ---------- completion: support restore
19:  625a4fac68 <  -:  ---------- doc: promote "git restore"

Nguyễn Thái Ngọc Duy (19):
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command switch
  switch: better names for -b and -B
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: no implicit dwim, use --guess to dwim
  switch: no worktree status unless real branch switch happens
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |  12 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                |  48 +-
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   4 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt                  | 257 ++++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  18 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  56 +--
 Makefile                                      |   1 +
 advice.c                                      |  11 +-
 builtin.h                                     |   1 +
 builtin/checkout.c                            | 464 +++++++++---------
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  26 +
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   3 +-
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |  16 +-
 t/t2060-switch.sh                             |  87 ++++
 38 files changed, 796 insertions(+), 372 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v2 01/19] git-checkout.txt: fix one syntax line
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ce7d38b8b7..bc7fbdb8d2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 02/19] doc: document --overwrite-ignore
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                     ` (17 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 5 +++++
 Documentation/git-merge.txt    | 4 ++++
 2 files changed, 9 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index bc7fbdb8d2..9ac01f7967 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -270,6 +270,11 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--[no-]overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use --no-overwrite-ignore to abort
+	the operation when the new branch contains ignored files.
+
 --[no-]recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..0480e9d0f0 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,10 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--[no-]overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use --no-overwrite-ignore to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                     ` (16 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v2 04/19] checkout: factor out some code in parse_branchname_arg()
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (2 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                     ` (15 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f63d864a91..12b7e08d4e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1068,6 +1068,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1075,10 +1103,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1200,26 +1226,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 05/19] checkout: make "opts" in cmd_checkout() a pointer
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (3 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                     ` (14 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 12b7e08d4e..66f99e788d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1326,82 +1326,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1409,14 +1410,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1425,7 +1426,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1444,56 +1445,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1512,6 +1513,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (4 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-15 22:38     ` Junio C Hamano
  2019-02-08  9:03   ` [PATCH v2 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                     ` (13 subsequent siblings)
  19 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 66f99e788d..5914dd3619 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -46,6 +46,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -57,6 +59,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1329,8 +1332,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1355,12 +1357,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1378,6 +1380,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1386,7 +1389,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1394,9 +1397,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 07/19] checkout: split options[] array in three pieces
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (5 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
                     ` (12 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  3 +-
 3 files changed, 78 insertions(+), 24 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5914dd3619..8ea1349cce 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1327,15 +1327,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1345,34 +1361,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1386,6 +1417,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index e2f3eaed07..76eb5c211b 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 14fe32428e..6846a5b1e0 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -237,7 +237,8 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 
-extern struct option *parse_options_concat(struct option *a, struct option *b);
+struct option *parse_options_dup(const struct option *a);
+struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 08/19] checkout: split part of it to new command switch
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (6 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                     ` (11 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and
something-to-checkout-paths. The good old "git checkout" command is
still here and will be until all (or most of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                        |   1 +
 Documentation/config/advice.txt   |  13 +-
 Documentation/config/branch.txt   |   4 +-
 Documentation/config/checkout.txt |   9 +-
 Documentation/config/diff.txt     |   3 +-
 Documentation/git-checkout.txt    |   4 +
 Documentation/git-switch.txt      | 257 ++++++++++++++++++++++++++++++
 Documentation/gitattributes.txt   |   3 +-
 Documentation/githooks.txt        |   8 +-
 Makefile                          |   1 +
 builtin.h                         |   1 +
 builtin/checkout.c                |  60 +++++--
 command-list.txt                  |   1 +
 git.c                             |   1 +
 14 files changed, 339 insertions(+), 27 deletions(-)
 create mode 100644 Documentation/git-switch.txt

diff --git a/.gitignore b/.gitignore
index 0d77ea5894..8e2b89047f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index 73380a8d86..fc211eca58 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 9ac01f7967..31c6cbef19 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -556,6 +556,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..1a4d156e3c
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,257 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c` or `-C`, or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to 'HEAD'). The operation is aborted
+however if the switch leads to loss of local changes, unless told
+otherwise.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The name of a commit at which to switch to before creating a
+	new branch or detach from.
++
+You can use the `"@{-N}"` syntax to refer to the N-th last
+branch/commit switched to "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `"@{-1}`.
++
+As a special case, you may use `"A...B"` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named <new-branch> starting at
+	<start-point> before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if <new-branch> already
+	exists, it will be reset to <start-point>. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+-g::
+--guess::
+	If <branch> is not found but there does exist a tracking
+	branch in exactly one remote (call it <remote>) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+-f::
+--force::
+	Proceed even if the index or the working tree differs from
+	HEAD. Both the index and working tree are restored to match
+	the switching target. This is used to throw away local
+	changes.
+
+--[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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See "--track" in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use "hack" as
+the local branch when branching off of "origin/hack" (or
+"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	branch.autoSetupMerge configuration variable is true.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named <new-branch>, started from
+	<start-point> and switch to it. See explanation of the same
+	option in linkgit:git-checkout[1] for details.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--[no-]recurse-submodules::
+	Using --recurse-submodules will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach the
+	submodules HEAD.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name, use `-g`:
+
+------------
+$ git switch -g new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit "HEAD~3" for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index b8392fc330..5731caa4c6 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 959044347e..e257a7810a 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -161,12 +161,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -402,7 +403,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 6e8d017e8e..0e5b29390c 100644
--- a/Makefile
+++ b/Makefile
@@ -789,6 +789,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8ea1349cce..2ac53b4302 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1396,33 +1401,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1555,3 +1552,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 0ce0e13f0f..31b8e8d6a1 100644
--- a/git.c
+++ b/git.c
@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 09/19] switch: better names for -b and -B
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (7 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-11  4:02     ` Eric Sunshine
  2019-02-08  9:03   ` [PATCH v2 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
                     ` (10 subsequent siblings)
  19 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout
out. Move -b/-B to cmd_checkout() and new -c/-C with the same
functionality in cmd_switch_branch()

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2ac53b4302..f6de232922 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 10/19] switch: remove -l
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (8 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                     ` (9 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f6de232922..f7d8fa7caa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1357,7 +1357,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1558,6 +1557,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 11/19] switch: stop accepting pathspec
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (9 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                     ` (8 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f7d8fa7caa..4822d5c5d7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -52,6 +52,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1162,10 +1163,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1199,11 +1206,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1248,7 +1256,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1564,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1591,6 +1600,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 12/19] switch: reject "do nothing" case
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (10 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                     ` (7 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4822d5c5d7..86e524f7c1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1319,6 +1320,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1572,6 +1579,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1601,6 +1609,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 13/19] switch: only allow explicit detached HEAD
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (11 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                     ` (6 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 86e524f7c1..d6c968f5f3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -44,6 +44,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1326,6 +1327,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("missing branch or commit argument"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die(_("a branch is expected, got %s"), new_branch_info->name);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1581,6 +1590,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1610,6 +1620,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 14/19] switch: add short option for --detach
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (12 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
                     ` (5 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index d6c968f5f3..a5284391a9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1381,7 +1381,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (13 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-10  8:37     ` Eric Sunshine
  2019-02-08  9:03   ` [PATCH v2 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                     ` (4 subsequent siblings)
  19 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

Similar to automatic detach, this behavior could be confusing because
it can sometimes create a new branch without a user asking it to,
especially when the user is still not aware about this feature.

In the future, perhaps we could have a config key to disable these
safety nets and let 'switch' do automatic detach or dwim
again. But that will be opt-in after the user knows what is what. For
now give a short option if you want to use it often.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 37 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++--------
 2 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 31c6cbef19..bcb7822c27 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	<branch>.
 +
 If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
-equivalent to
+exactly one remote (call it <remote>) with a matching name and
+--no-guess is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit <branch>, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	branch.autoSetupMerge configuration variable is true.
 
+--[no-]guess::
+	If <branch> is not found but there does exist a tracking
+	branch in exactly one remote (call it <remote>) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -284,10 +295,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach the
 	submodules HEAD.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --[no-]overlay::
 	In the default overlay mode, `git checkout` never
 	removes files from the index or the working tree.  When
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a5284391a9..a49ab35b7d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -52,7 +52,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 
@@ -1388,8 +1388,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1426,7 +1424,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1441,7 +1438,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1501,7 +1497,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1582,12 +1578,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1612,12 +1610,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL('g', "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 16/19] switch: no worktree status unless real branch switch happens
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (14 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:03   ` [PATCH v2 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
                     ` (3 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 127 +++---------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 9 insertions(+), 140 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index fc211eca58..2fe24049fe 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -17,14 +17,6 @@ and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
 
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
-
 checkout.overlayMode::
 	In the default overlay mode, `git checkout` never
 	removes files from the index or the working tree.  When
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a49ab35b7d..21a3fe3fb8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -25,8 +25,6 @@
 #include "submodule.h"
 #include "advice.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -55,11 +53,7 @@ struct checkout_opts {
 	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -556,104 +550,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1011,6 +907,8 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
+
 	memset(&old_branch_info, 0, sizeof(old_branch_info));
 	old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
 	if (old_branch_info.path)
@@ -1027,16 +925,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1058,11 +952,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 {
 	struct checkout_opts *opts = cb;
 
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "checkout.overlaymode")) {
 		opts->overlay_mode = git_config_bool(var, value);
 		return 0;
@@ -1587,6 +1476,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1620,6 +1510,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 17/19] t: add tests for switch
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (15 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:03   ` Nguyễn Thái Ngọc Duy
  2019-02-08  9:04   ` [PATCH v2 18/19] completion: support switch Nguyễn Thái Ngọc Duy
                     ` (2 subsequent siblings)
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:03 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100755 t/t2060-switch.sh

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..1e1e834c1b
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch to a commit' '
+	test_must_fail git switch master^{commit}
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch' '
+	test_when_finished git switch master &&
+	git switch --orphan new-orphan master^ &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_commit expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch foo &&
+	git switch --guess foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_done
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 18/19] completion: support switch
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (16 preceding siblings ...)
  2019-02-08  9:03   ` [PATCH v2 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:04   ` Nguyễn Thái Ngọc Duy
  2019-02-08 14:19     ` SZEDER Gábor
  2019-02-08  9:04   ` [PATCH v2 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
  19 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:04 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 499e56f83d..891abb72d7 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2126,6 +2126,32 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if ---guess was specified to enable DWIM mode
+		local track_opt= only_local_ref=n
+		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
+			track_opt='--track'
+		elif [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v2 19/19] doc: promote "git switch"
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (17 preceding siblings ...)
  2019-02-08  9:04   ` [PATCH v2 18/19] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-02-08  9:04   ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
  19 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-02-08  9:04 UTC (permalink / raw)
  To: pclouds; +Cc: git, Eric Sunshine, SZEDER Gábor, Junio C Hamano

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-branch.txt           | 12 +++---
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 ++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 18 ++++-----
 Documentation/giteveryday.txt          | 24 +++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 56 +++++++++++++-------------
 advice.c                               | 11 +++--
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             | 16 ++++----
 17 files changed, 100 insertions(+), 95 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bf5316ffa9..3cf6153415 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -293,7 +293,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -318,9 +318,9 @@ is currently checked out) does not have all commits from the test branch.
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 27304428a1..3d42853529 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4dd5853d6e..420777c10b 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 9f69ae8b69..020ba838c9 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..6bd0f192cc 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git reset --hard master^2
-$ git checkout master
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..38015026bb 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch pu && git reset --hard next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..94799faa2b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index 567209aa79..457c1a255e 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: switching to '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by switching back to a branch.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -c with the switch command. Example:\n"
+	"\n"
+	"  git switch -c <new-branch-name>\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/sha1-name.c b/sha1-name.c
index a656481c6a..e5f0832995 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -745,7 +745,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..f85dbd6740 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,16 +195,16 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit three
 	EOF
@@ -271,16 +271,16 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit... three
 	EOF
-- 
2.20.1.682.gd5861c6d90


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v2 18/19] completion: support switch
  2019-02-08  9:04   ` [PATCH v2 18/19] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-02-08 14:19     ` SZEDER Gábor
  2019-02-09  5:30       ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: SZEDER Gábor @ 2019-02-08 14:19 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Eric Sunshine, Junio C Hamano

On Fri, Feb 08, 2019 at 04:04:00PM +0700, Nguyễn Thái Ngọc Duy wrote:
> Completion support for --guess could be made better. If no --detach is
> given, we should only provide a list of refs/heads/* and dwim ones,
> not the entire ref space. But I still can't penetrate that
> __git_refs() function yet.
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index 499e56f83d..891abb72d7 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -2126,6 +2126,32 @@ _git_status ()
>  	__git_complete_index_file "$complete_opt"
>  }
>  
> +_git_switch ()
> +{
> +	case "$cur" in
> +	--conflict=*)
> +		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
> +		;;
> +	--*)
> +		__gitcomp_builtin switch
> +		;;
> +	*)
> +		# check if ---guess was specified to enable DWIM mode

Nit: s/---/--/

> +		local track_opt= only_local_ref=n
> +		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
> +			track_opt='--track'
> +		elif [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
> +			only_local_ref=y
> +		fi

Could these two options be used together?  I think they could.  If
that's the case, then the two conditions shouldn't be chained with
elif, but should be two separate if statements (even eliminating
$only_local_ref, while at it?).  If that's not the case, then the two
__git_find_on_cmdline() calls could be combined into one, and a case
statement could act according the option found, sparing one of the
subshells from the two.

> +		if [ $only_local_ref = y ]; then
> +			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
> +		else
> +			__git_complete_refs $track_opt
> +		fi
> +		;;
> +	esac
> +}
> +
>  __git_config_get_set_variables ()
>  {
>  	local prevword word config_file= c=$cword
> -- 
> 2.20.1.682.gd5861c6d90
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2 18/19] completion: support switch
  2019-02-08 14:19     ` SZEDER Gábor
@ 2019-02-09  5:30       ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-02-09  5:30 UTC (permalink / raw)
  To: SZEDER Gábor; +Cc: Git Mailing List, Eric Sunshine, Junio C Hamano

On Fri, Feb 8, 2019 at 9:19 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
>
> On Fri, Feb 08, 2019 at 04:04:00PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > Completion support for --guess could be made better. If no --detach is
> > given, we should only provide a list of refs/heads/* and dwim ones,
> > not the entire ref space. But I still can't penetrate that
> > __git_refs() function yet.
> >
> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> > ---
> >  contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++
> >  1 file changed, 26 insertions(+)
> >
> > diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> > index 499e56f83d..891abb72d7 100644
> > --- a/contrib/completion/git-completion.bash
> > +++ b/contrib/completion/git-completion.bash
> > @@ -2126,6 +2126,32 @@ _git_status ()
> >       __git_complete_index_file "$complete_opt"
> >  }
> >
> > +_git_switch ()
> > +{
> > +     case "$cur" in
> > +     --conflict=*)
> > +             __gitcomp "diff3 merge" "" "${cur##--conflict=}"
> > +             ;;
> > +     --*)
> > +             __gitcomp_builtin switch
> > +             ;;
> > +     *)
> > +             # check if ---guess was specified to enable DWIM mode
>
> Nit: s/---/--/
>
> > +             local track_opt= only_local_ref=n
> > +             if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
> > +                     track_opt='--track'
> > +             elif [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
> > +                     only_local_ref=y
> > +             fi
>
> Could these two options be used together?  I think they could.

It does not make much sense when dwim is active since you'll be
creating a new branch then detach from it. But yeah when you give a
real branch, no dwim, no new branch created, then "git switch -dg"
should work. Will fix.

> If
> that's the case, then the two conditions shouldn't be chained with
> elif, but should be two separate if statements (even eliminating
> $only_local_ref, while at it?).  If that's not the case, then the two
> __git_find_on_cmdline() calls could be combined into one, and a case
> statement could act according the option found, sparing one of the
> subshells from the two.
>
> > +             if [ $only_local_ref = y ]; then
> > +                     __gitcomp_direct "$(__git_heads "" "$cur" " ")"
> > +             else
> > +                     __git_complete_refs $track_opt
> > +             fi
> > +             ;;
> > +     esac
> > +}
> > +
> >  __git_config_get_set_variables ()
> >  {
> >       local prevword word config_file= c=$cword
> > --
> > 2.20.1.682.gd5861c6d90
> >
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim
  2019-02-08  9:03   ` [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
@ 2019-02-10  8:37     ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-02-10  8:37 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, SZEDER Gábor, Junio C Hamano

On Fri, Feb 8, 2019 at 4:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Similar to automatic detach, this behavior could be confusing because
> it can sometimes create a new branch without a user asking it to,
> especially when the user is still not aware about this feature.
> [...]
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
> @@ -31,22 +31,13 @@ branch.
>  If <branch> is not found but there does exist a tracking branch in
> +exactly one remote (call it <remote>) with a matching name and
> +--no-guess is not specified, treat as equivalent to

For consistency with typesetting in the rest of this document, you
probably want s/--no-guess/`&`/, though probably not worth a re-roll,
though, if this is the only issue with v2.

> @@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.
> +--[no-]guess::
> +       If <branch> is not found but there does exist a tracking
> +       branch in exactly one remote (call it <remote>) with a
> +       matching name, treat as equivalent to

Most places in this document typeset these as `<branch>` and `<remote>`.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2 09/19] switch: better names for -b and -B
  2019-02-08  9:03   ` [PATCH v2 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-02-11  4:02     ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-02-11  4:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, SZEDER Gábor, Junio C Hamano

On Fri, Feb 8, 2019 at 4:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> The shortcut of these options do not make much sense when used with
> switch. And their descriptions are also tied to checkout
> out. [...]

"checkout out"?

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-02-08  9:03   ` [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-02-15 22:38     ` Junio C Hamano
  0 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-02-15 22:38 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, SZEDER Gábor

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> These local variables are referenced by struct option[]. This struct
> will soon be broken down, moved away and we can't rely on local
> variables anymore. Move these two to struct checkout_opts in
> preparation for that.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

Up to this point all patches made sense to me.  Thanks for working
on this.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v3 00/21] Add new command "switch"
  2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
                     ` (18 preceding siblings ...)
  2019-02-08  9:04   ` [PATCH v2 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57   ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 01/21] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
                       ` (22 more replies)
  19 siblings, 23 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

v3 contains document and completion updates based on v2's feedback. It
also contains some extra git-checkout.txt updates (blame Eric for this,
he points out problems in git-switch.txt and makes me want to go fix
git-checkout.txt too).

The series is now based on 'master' (yay!)

Nguyễn Thái Ngọc Duy (21):
  git-checkout.txt: spell out --no-option
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  git-checkout.txt: fix monospace typeset
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command 'switch'
  switch: better names for -b and -B
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: no implicit dwim, use --guess to dwim
  switch: no worktree status unless real branch switch happens
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |  12 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                | 119 +++--
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   5 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt                  | 259 ++++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  19 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/gitworkflows.txt                |   3 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  56 +--
 Makefile                                      |   1 +
 advice.c                                      |  11 +-
 builtin.h                                     |   1 +
 builtin/checkout.c                            | 471 +++++++++---------
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  27 +
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   1 +
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |  16 +-
 t/t2060-switch.sh                             |  87 ++++
 39 files changed, 837 insertions(+), 415 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh

Range-diff dựa trên v2:
 -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option
 1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line
 2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore
    @@ -14,14 +14,15 @@
      	out anyway. In other words, the ref can be held by more than one
      	worktree.
      
    -+--[no-]overwrite-ignore::
    ++--overwrite-ignore::
    ++--no-overwrite-ignore::
     +	Silently overwrite ignored files when switching branches. This
    -+	is the default behavior. Use --no-overwrite-ignore to abort
    ++	is the default behavior. Use `--no-overwrite-ignore` to abort
     +	the operation when the new branch contains ignored files.
     +
    - --[no-]recurse-submodules::
    + --recurse-submodules::
    + --no-recurse-submodules::
      	Using --recurse-submodules will update the content of all initialized
    - 	submodules according to the commit recorded in the superproject. If
     
      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
      --- a/Documentation/git-merge.txt
    @@ -30,9 +31,10 @@
      	Allow the rerere mechanism to update the index with the
      	result of auto-conflict resolution if possible.
      
    -+--[no-]overwrite-ignore::
    ++--overwrite-ignore::
    ++--no-overwrite-ignore::
     +	Silently overwrite ignored files from the merge result. This
    -+	is the default behavior. Use --no-overwrite-ignore to abort.
    ++	is the default behavior. Use `--no-overwrite-ignore` to abort.
     +
      --abort::
      	Abort the current conflict resolution process, and
 -:  ---------- >  4:  5b165524d1 git-checkout.txt: fix monospace typeset
 3:  1e34862ad8 =  5:  a4240a888e t: rename t2014-switch.sh to t2014-checkout-switch.sh
 4:  aba8f41f73 =  6:  dc4a8e8933 checkout: factor out some code in parse_branchname_arg()
 5:  f165322652 =  7:  f0d933c2ac checkout: make "opts" in cmd_checkout() a pointer
 6:  75f9a38e85 =  8:  0aa541689c checkout: move 'confict_style' and 'dwim_..' to checkout_opts
 7:  9188718493 =  9:  84ca42bb26 checkout: split options[] array in three pieces
 8:  b5af7d22fd ! 10:  e983c8bb2c checkout: split part of it to new command switch
    @@ -1,6 +1,6 @@
     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
     
    -    checkout: split part of it to new command switch
    +    checkout: split part of it to new command 'switch'
     
         "git checkout" doing too many things is a source of confusion for many
         users (and it even bites old timers sometimes). To remedy that, the
    @@ -180,7 +180,7 @@
     ++
     +You can use the `"@{-N}"` syntax to refer to the N-th last
     +branch/commit switched to "git switch" or "git checkout"
    -+operation. You may also specify `-` which is synonymous to `"@{-1}`.
    ++operation. You may also specify `-` which is synonymous to `"@{-1}"`.
     ++
     +As a special case, you may use `"A...B"` as a shortcut for the merge
     +base of `A` and `B` if there is exactly one merge base. You can leave
    @@ -188,8 +188,8 @@
     +
     +-c <new-branch>::
     +--create <new-branch>::
    -+	Create a new branch named <new-branch> starting at
    -+	<start-point> before switching to the branch. This is a
    ++	Create a new branch named `<new-branch>` starting at
    ++	`<start-point>` before switching to the branch. This is a
     +	convenient shortcut for:
     ++
     +------------
    @@ -199,8 +199,8 @@
     +
     +-C <new-branch>::
     +--force-create <new-branch>::
    -+	Similar to `--create` except that if <new-branch> already
    -+	exists, it will be reset to <start-point>. This is a
    ++	Similar to `--create` except that if `<new-branch>` already
    ++	exists, it will be reset to `<start-point>`. This is a
     +	convenient shortcut for:
     ++
     +------------
    @@ -216,8 +216,8 @@
     +
     +-g::
     +--guess::
    -+	If <branch> is not found but there does exist a tracking
    -+	branch in exactly one remote (call it <remote>) with a
    ++	If `<branch>` is not found but there does exist a tracking
    ++	branch in exactly one remote (call it `<remote>`) with a
     +	matching name, treat as equivalent to
     ++
     +------------
    @@ -243,7 +243,8 @@
     +	the switching target. This is used to throw away local
     +	changes.
     +
    -+--[no-]progress::
    ++--progress::
    ++--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
    @@ -292,8 +293,8 @@
     +	"merge" style, shows the original contents).
     +
     +--orphan <new-branch>::
    -+	Create a new 'orphan' branch, named <new-branch>, started from
    -+	<start-point> and switch to it. See explanation of the same
    ++	Create a new 'orphan' branch, named `<new-branch>`, started from
    ++	`<start-point>` and switch to it. See explanation of the same
     +	option in linkgit:git-checkout[1] for details.
     +
     +--ignore-other-worktrees::
    @@ -302,7 +303,8 @@
     +	the ref out anyway. In other words, the ref can be held by
     +	more than one worktree.
     +
    -+--[no-]recurse-submodules::
    ++--recurse-submodules::
    ++--no-recurse-submodules::
     +	Using --recurse-submodules will update the content of all initialized
     +	submodules according to the commit recorded in the superproject. If
     +	local modifications in a submodule would be overwritten the checkout
 9:  5be400099c ! 11:  5491932cc8 switch: better names for -b and -B
    @@ -3,9 +3,9 @@
         switch: better names for -b and -B
     
         The shortcut of these options do not make much sense when used with
    -    switch. And their descriptions are also tied to checkout
    -    out. Move -b/-B to cmd_checkout() and new -c/-C with the same
    -    functionality in cmd_switch_branch()
    +    switch. And their descriptions are also tied to checkout. Move -b/-B
    +    to cmd_checkout() and new -c/-C with the same functionality in
    +    cmd_switch_branch()
     
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
10:  38cfc1fada = 12:  e0f1247b09 switch: remove -l
11:  7f0abab72c = 13:  f7ff1a7df6 switch: stop accepting pathspec
12:  386ba95007 = 14:  0750d63d38 switch: reject "do nothing" case
13:  e488c7d8ca = 15:  bcd843146d switch: only allow explicit detached HEAD
14:  9cd5f63c01 = 16:  d5cc55525d switch: add short option for --detach
15:  fc31b3d4f3 ! 17:  4a27400a58 switch: no implicit dwim, use --guess to dwim
    @@ -15,13 +15,13 @@
      --- a/Documentation/git-checkout.txt
      +++ b/Documentation/git-checkout.txt
     @@
    - 	<branch>.
    + 	`<branch>`.
      +
    - If <branch> is not found but there does exist a tracking branch in
    --exactly one remote (call it <remote>) with a matching name, treat as
    + If `<branch>` is not found but there does exist a tracking branch in
    +-exactly one remote (call it `<remote>`) with a matching name, treat as
     -equivalent to
    -+exactly one remote (call it <remote>) with a matching name and
    -+--no-guess is not specified, treat as equivalent to
    ++exactly one remote (call it `<remote>`) with a matching name and
    ++`--no-guess` is not specified, treat as equivalent to
      +
      ------------
      $ git checkout -b <branch> --track <remote>/<branch>
    @@ -36,16 +36,17 @@
     -'origin' remote. See also `checkout.defaultRemote` in
     -linkgit:git-config[1].
     -+
    - You could omit <branch>, in which case the command degenerates to
    + You could omit `<branch>`, in which case the command degenerates to
      "check out the current branch", which is a glorified no-op with
      rather expensive side-effects to show only the tracking information,
     @@
      	Do not set up "upstream" configuration, even if the
      	branch.autoSetupMerge configuration variable is true.
      
    -+--[no-]guess::
    -+	If <branch> is not found but there does exist a tracking
    -+	branch in exactly one remote (call it <remote>) with a
    ++--guess::
    ++--no-guess::
    ++	If `<branch>` is not found but there does exist a tracking
    ++	branch in exactly one remote (call it `<remote>`) with a
     +	matching name, treat as equivalent to
     ++
     +------------
    @@ -74,9 +75,9 @@
     -	Do not attempt to create a branch if a remote tracking branch
     -	of the same name exists.
     -
    - --[no-]overlay::
    + --overlay::
    + --no-overlay::
      	In the default overlay mode, `git checkout` never
    - 	removes files from the index or the working tree.  When
     
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
16:  761f953b40 = 18:  f7dd33abe6 switch: no worktree status unless real branch switch happens
17:  206f1bda79 = 19:  5b182abea8 t: add tests for switch
18:  3cfb2913eb ! 20:  1575064fe6 completion: support switch
    @@ -28,10 +28,11 @@
     +		local track_opt= only_local_ref=n
     +		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
     +			track_opt='--track'
    -+		elif [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
    ++		fi
    ++		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
     +			only_local_ref=y
     +		fi
    -+		if [ $only_local_ref = y ]; then
    ++		if [ $only_local_ref = y -a -n "$track_opt"]; then
     +			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
     +		else
     +			__git_complete_refs $track_opt
19:  2d018878bd ! 21:  8ed01ad0c5 doc: promote "git switch"
    @@ -317,9 +317,9 @@
      
      ------------
     -$ git checkout mybranch
    -+$ git switch mybranch
    - $ git reset --hard master^2
    +-$ git reset --hard master^2
     -$ git checkout master
    ++$ git switch -C mybranch master^2
     +$ git switch master
      $ git reset --hard master^
      ------------
    @@ -400,7 +400,7 @@
     -$ git checkout pu && git reset --hard next <7>
     +$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
     +$ git switch topic/one && git rebase master <6>
    -+$ git switch pu && git reset --hard next <7>
    ++$ git switch -C pu next <7>
      $ git merge topic/one topic/two && git merge hold/linus <8>
     -$ git checkout maint
     +$ git switch maint
    @@ -430,6 +430,20 @@
      
      Check that the change you made is no longer visible, since it was
     
    + diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
    + --- a/Documentation/gitworkflows.txt
    + +++ b/Documentation/gitworkflows.txt
    +@@
    + .Rewind and rebuild next
    + [caption="Recipe: "]
    + =====================================
    +-* `git checkout next`
    +-* `git reset --hard master`
    ++* `git switch -C next master`
    + * `git merge ai/topic_in_next1`
    + * `git merge ai/topic_in_next2`
    + * ...
    +
      diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
      --- a/Documentation/revisions.txt
      +++ b/Documentation/revisions.txt
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v3 01/21] git-checkout.txt: spell out --no-option
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-10  3:28       ` Eric Sunshine
  2019-03-08  9:57     ` [PATCH v3 02/21] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                       ` (21 subsequent siblings)
  22 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

It's easier to search for and also less cryptic.
---
 Documentation/git-checkout.txt | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index f179b43732..99c8c0dc0f 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,7 +118,8 @@ OPTIONS
 --quiet::
 	Quiet, suppress feedback messages.
 
---[no-]progress::
+--progress::
+--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
@@ -262,7 +263,7 @@ 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.
+`--overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
---[no-]recurse-submodules::
+--recurse-submodules::
+--no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also
 	Do not attempt to create a branch if a remote tracking branch
 	of the same name exists.
 
---[no-]overlay::
+--overlay::
+--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
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 02/21] git-checkout.txt: fix one syntax line
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 01/21] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 03/21] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                       ` (20 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 99c8c0dc0f..28817cfa41 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 03/21] doc: document --overwrite-ignore
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 01/21] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 02/21] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 04/21] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
                       ` (19 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.
---
 Documentation/git-checkout.txt | 6 ++++++
 Documentation/git-merge.txt    | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 28817cfa41..5280d1f9ed 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort
+	the operation when the new branch contains ignored files.
+
 --recurse-submodules::
 --no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..6a9163d8fe 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,11 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 04/21] git-checkout.txt: fix monospace typeset
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (2 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 03/21] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-09 12:35       ` Martin Ågren
  2019-03-08  9:57     ` [PATCH v3 05/21] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                       ` (18 subsequent siblings)
  22 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

---
 Documentation/git-checkout.txt | 60 +++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 5280d1f9ed..1b9d689933 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-	To prepare for working on <branch>, switch to it by updating
+	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
-	<branch>.
+	`<branch>`.
 +
-If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
+If `<branch>` is not found but there does exist a tracking branch in
+exactly one remote (call it `<remote>`) with a matching name, treat as
 equivalent to
 +
 ------------
@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the
 'origin' remote. See also `checkout.defaultRemote` in
 linkgit:git-config[1].
 +
-You could omit <branch>, in which case the command degenerates to
+You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
@@ -61,7 +61,7 @@ if exists, for the current branch.
 	`--track` without `-b` implies branch creation; see the
 	description of `--track` below.
 +
-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
 is reset. This is the transactional equivalent of
 +
 ------------
@@ -75,25 +75,25 @@ successful.
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of <commit>, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching HEAD at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
 	tree will be the state recorded in the commit plus the local
 	modifications.
 +
-When the <commit> argument is a branch name, the `--detach` option can
+When the `<commit>` argument is a branch name, the `--detach` option can
 be used to detach HEAD at the tip of the branch (`git checkout
-<branch>` would check out that branch without detaching HEAD).
+`<branch>`` would check out that branch without detaching HEAD).
 +
-Omitting <branch> detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches HEAD at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
 	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the <tree-ish> (most often a
-	commit).  When a <tree-ish> is given, the paths that
-	match the <pathspec> are updated both in the index and in
+	contents in the index or in the `<tree-ish>` (most often a
+	commit).  When a `<tree-ish>` is given, the paths that
+	match the `<pathspec>` are updated both in the index and in
 	the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new_branch>::
-	Create a new branch named <new_branch> and start it at
-	<start_point>; see linkgit:git-branch[1] for details.
+	Create a new branch named `<new_branch>` and start it at
+	`<start_point>`; see linkgit:git-branch[1] for details.
 
 -B <new_branch>::
-	Creates the branch <new_branch> and start it at <start_point>;
-	if it already exists, then reset it to <start_point>. This is
+	Creates the branch `<new_branch>` and start it at `<start_point>`;
+	if it already exists, then reset it to `<start_point>`. This is
 	equivalent to running "git branch" with "-f"; see
 	linkgit:git-branch[1] for details.
 
@@ -191,19 +191,19 @@ explicitly give a name with `-b` in such a case.
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
 	This is the default behavior of "git checkout <commit>" when
-	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
 --orphan <new_branch>::
-	Create a new 'orphan' branch, named <new_branch>, started from
-	<start_point> and switch to it.  The first commit made on this
+	Create a new 'orphan' branch, named `<new_branch>`, started from
+	`<start_point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
 "git checkout <start_point>".  This allows you to start a new history
-that records a set of paths similar to <start_point> by easily running
+that records a set of paths similar to `<start_point>` by easily running
 "git commit -a" to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
@@ -213,7 +213,7 @@ whose full history contains proprietary or otherwise encumbered bits of
 code.
 +
 If you want to start a disconnected history that records a set of paths
-that is totally different from the one of <start_point>, then you should
+that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
 branch by running "git rm -rf ." from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
@@ -221,9 +221,9 @@ working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
-	update only entries matched by <paths> and sparse patterns
+	update only entries matched by `<paths>` and sparse patterns
 	in $GIT_DIR/info/sparse-checkout. This option ignores
-	the sparse patterns and adds back any files in <paths>.
+	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
 --merge::
@@ -254,9 +254,9 @@ the conflicted merge in the specified paths.
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	<tree-ish> (or the index, if unspecified) and the working
+	`<tree-ish>` (or the index, if unspecified) and the working
 	tree.  The chosen hunks are then applied in reverse to the
-	working tree (and if a <tree-ish> was specified, the index).
+	working tree (and if a `<tree-ish>` was specified, the index).
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
@@ -279,10 +279,10 @@ Note that this option uses the no overlay mode by default (see also
 
 --recurse-submodules::
 --no-recurse-submodules::
-	Using --recurse-submodules will update the content of all initialized
+	Using `--recurse-submodules` will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
 	Just like linkgit:git-submodule[1], this will detach the
 	submodules HEAD.
@@ -296,8 +296,8 @@ Note that this option uses the no overlay mode by default (see also
 	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.
+	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,
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 05/21] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (3 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 04/21] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 06/21] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                       ` (17 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v3 06/21] checkout: factor out some code in parse_branchname_arg()
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (4 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 05/21] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 07/21] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                       ` (16 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0e6037b296..cbdcb1417f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 07/21] checkout: make "opts" in cmd_checkout() a pointer
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (5 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 06/21] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 08/21] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                       ` (15 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index cbdcb1417f..baefe3c942 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 08/21] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (6 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 07/21] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 09/21] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                       ` (14 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index baefe3c942..4b43433941 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -47,6 +47,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -58,6 +60,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 09/21] checkout: split options[] array in three pieces
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (7 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 08/21] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
                       ` (13 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  1 +
 3 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4b43433941..7d23083282 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 2733393546..caaeed896f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 7d83e2971d..9a90c332a5 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+struct option *parse_options_dup(const struct option *a);
 struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (8 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 09/21] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-09 12:36       ` Martin Ågren
                         ` (3 more replies)
  2019-03-08  9:57     ` [PATCH v3 11/21] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                       ` (12 subsequent siblings)
  22 siblings, 4 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and
something-to-checkout-paths. The good old "git checkout" command is
still here and will be until all (or most of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.
---
 .gitignore                        |   1 +
 Documentation/config/advice.txt   |  13 +-
 Documentation/config/branch.txt   |   4 +-
 Documentation/config/checkout.txt |   9 +-
 Documentation/config/diff.txt     |   3 +-
 Documentation/git-checkout.txt    |   4 +
 Documentation/git-switch.txt      | 259 ++++++++++++++++++++++++++++++
 Documentation/gitattributes.txt   |   3 +-
 Documentation/githooks.txt        |   8 +-
 Makefile                          |   1 +
 builtin.h                         |   1 +
 builtin/checkout.c                |  60 +++++--
 command-list.txt                  |   1 +
 git.c                             |   1 +
 14 files changed, 341 insertions(+), 27 deletions(-)
 create mode 100644 Documentation/git-switch.txt

diff --git a/.gitignore b/.gitignore
index 7374587f9d..c687b92b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index c4118fa196..d6872ffa83 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 1b9d689933..ac355dc3f3 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -560,6 +560,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..ff60ba69fb
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,259 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c` or `-C`, or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to 'HEAD'). The operation is aborted
+however if the switch leads to loss of local changes, unless told
+otherwise.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The name of a commit at which to switch to before creating a
+	new branch or detach from.
++
+You can use the `"@{-N}"` syntax to refer to the N-th last
+branch/commit switched to "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `"@{-1}"`.
++
+As a special case, you may use `"A...B"` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named `<new-branch>` starting at
+	`<start-point>` before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if `<new-branch>` already
+	exists, it will be reset to `<start-point>`. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+-g::
+--guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+-f::
+--force::
+	Proceed even if the index or the working tree differs from
+	HEAD. Both the index and working tree are restored to match
+	the switching target. This is used to throw away local
+	changes.
+
+--progress::
+--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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See "--track" in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use "hack" as
+the local branch when branching off of "origin/hack" (or
+"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	branch.autoSetupMerge configuration variable is true.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named `<new-branch>`, started from
+	`<start-point>` and switch to it. See explanation of the same
+	option in linkgit:git-checkout[1] for details.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using --recurse-submodules will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach the
+	submodules HEAD.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name, use `-g`:
+
+------------
+$ git switch -g new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit "HEAD~3" for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 9b41f81c06..cd0f9fa507 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 5bf653c111..8ff72f0613 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -406,7 +407,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 148668368b..8e91db73ad 100644
--- a/Makefile
+++ b/Makefile
@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 7d23083282..1eff10dbef 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 2014aab6b8..39582cf511 100644
--- a/git.c
+++ b/git.c
@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 11/21] switch: better names for -b and -B
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (9 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 12/21] switch: remove -l Nguyễn Thái Ngọc Duy
                       ` (11 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout. Move -b/-B
to cmd_checkout() and new -c/-C with the same functionality in
cmd_switch_branch()
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1eff10dbef..4c3f0f6ac7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 12/21] switch: remove -l
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (10 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 11/21] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 13/21] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                       ` (10 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4c3f0f6ac7..a731f983c4 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1372,7 +1372,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1573,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 13/21] switch: stop accepting pathspec
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (11 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 12/21] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 14/21] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                       ` (9 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index a731f983c4..1b1181b220 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1175,10 +1176,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1212,11 +1219,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1261,7 +1269,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1579,6 +1587,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1606,6 +1615,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 14/21] switch: reject "do nothing" case
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (12 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 13/21] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 15/21] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                       ` (8 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1b1181b220..f9f7ee2936 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -54,6 +54,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1334,6 +1335,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1587,6 +1594,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1616,6 +1624,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 15/21] switch: only allow explicit detached HEAD
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (13 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 14/21] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 16/21] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                       ` (7 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)
---
 builtin/checkout.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9f7ee2936..2e150f0175 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -45,6 +45,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1341,6 +1342,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("missing branch or commit argument"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die(_("a branch is expected, got %s"), new_branch_info->name);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1596,6 +1605,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1625,6 +1635,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 16/21] switch: add short option for --detach
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (14 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 15/21] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
                       ` (6 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2e150f0175..0866aeba83 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1396,7 +1396,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (15 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 16/21] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-13 18:36       ` Eckhard Maaß
  2019-03-08  9:57     ` [PATCH v3 18/21] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                       ` (5 subsequent siblings)
  22 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

Similar to automatic detach, this behavior could be confusing because
it can sometimes create a new branch without a user asking it to,
especially when the user is still not aware about this feature.

In the future, perhaps we could have a config key to disable these
safety nets and let 'switch' do automatic detach or dwim
again. But that will be opt-in after the user knows what is what. For
now give a short option if you want to use it often.
---
 Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++-------
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ac355dc3f3..2b776c1269 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	`<branch>`.
 +
 If `<branch>` is not found but there does exist a tracking branch in
-exactly one remote (call it `<remote>`) with a matching name, treat as
-equivalent to
+exactly one remote (call it `<remote>`) with a matching name and
+`--no-guess` is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	branch.autoSetupMerge configuration variable is true.
 
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach the
 	submodules HEAD.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --overlay::
 --no-overlay::
 	In the default overlay mode, `git checkout` never
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0866aeba83..8a89df4f36 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,7 +53,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 
@@ -1403,8 +1403,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1441,7 +1439,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1456,7 +1453,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1516,7 +1512,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1597,12 +1593,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1627,12 +1625,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL('g', "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 18/21] switch: no worktree status unless real branch switch happens
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (16 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-10  3:43       ` Eric Sunshine
  2019-03-08  9:57     ` [PATCH v3 19/21] t: add tests for switch Nguyễn Thái Ngọc Duy
                       ` (4 subsequent siblings)
  22 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 134 ++----------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 8 insertions(+), 148 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index d6872ffa83..6b646813ab 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
-
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8a89df4f36..4903359b49 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -26,8 +26,6 @@
 #include "submodule.h"
 #include "advice.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -56,11 +54,7 @@ struct checkout_opts {
 	int dwim_new_local_branch;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -564,112 +558,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	 /*
-	  * Do the merge if this is the initial checkout. We cannot use
-	  * is_cache_unborn() here because the index hasn't been loaded yet
-	  * so cache_nr and timestamp.sec are always zero.
-	  */
-	if (!file_exists(get_index_file()))
-		return 0;
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1027,6 +915,7 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
 
 	trace2_cmd_mode("branch");
 
@@ -1046,16 +935,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1075,11 +960,6 @@ static int switch_branches(const struct checkout_opts *opts,
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "diff.ignoresubmodules")) {
 		struct checkout_opts *opts = cb;
 		handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1602,6 +1482,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1635,6 +1516,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 19/21] t: add tests for switch
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (17 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 18/21] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-10  3:59       ` Eric Sunshine
  2019-03-10 10:09       ` Andrei Rybak
  2019-03-08  9:57     ` [PATCH v3 20/21] completion: support switch Nguyễn Thái Ngọc Duy
                       ` (3 subsequent siblings)
  22 siblings, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

---
 t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100755 t/t2060-switch.sh

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..1e1e834c1b
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch to a commit' '
+	test_must_fail git switch master^{commit}
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch' '
+	test_when_finished git switch master &&
+	git switch --orphan new-orphan master^ &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_commit expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch foo &&
+	git switch --guess foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_done
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 20/21] completion: support switch
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (18 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 19/21] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08  9:57     ` [PATCH v3 21/21] doc: promote "git switch" Nguyễn Thái Ngọc Duy
                       ` (2 subsequent siblings)
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.
---
 contrib/completion/git-completion.bash | 27 ++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 976e4a6548..7fcf28d437 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2158,6 +2158,33 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if ---guess was specified to enable DWIM mode
+		local track_opt= only_local_ref=n
+		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
+			track_opt='--track'
+		fi
+		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y -a -n "$track_opt"]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v3 21/21] doc: promote "git switch"
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (19 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 20/21] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-03-08  9:57     ` Nguyễn Thái Ngọc Duy
  2019-03-08 17:48     ` [PATCH v3 00/21] Add new command "switch" Ramsay Jones
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
  22 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-08  9:57 UTC (permalink / raw)
  To: pclouds; +Cc: git, gitster, sunshine, szeder.dev

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...
---
 Documentation/git-branch.txt           | 12 +++---
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 ++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 19 +++++----
 Documentation/giteveryday.txt          | 24 +++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/gitworkflows.txt         |  3 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 56 +++++++++++++-------------
 advice.c                               | 11 +++--
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             | 16 ++++----
 18 files changed, 101 insertions(+), 98 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0cd87ddeff..1e2d89b174 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -297,7 +297,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 1af85d404f..0a24a5679e 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5629ba4c5d..cb6fc166e2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 132f8e55f6..cbf901efb4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..f880d21dfb 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
+$ git switch -C mybranch master^2
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..ad455f3e39 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch -C pu next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index ca11c7bdaf..abc0dc6bc7 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -301,8 +301,7 @@ topics on 'next':
 .Rewind and rebuild next
 [caption="Recipe: "]
 =====================================
-* `git checkout next`
-* `git reset --hard master`
+* `git switch -C next master`
 * `git merge ai/topic_in_next1`
 * `git merge ai/topic_in_next2`
 * ...
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..94799faa2b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index 567209aa79..457c1a255e 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: switching to '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by switching back to a branch.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -c with the switch command. Example:\n"
+	"\n"
+	"  git switch -c <new-branch-name>\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/sha1-name.c b/sha1-name.c
index 6dda2c16df..da0518c8e3 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..f85dbd6740 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,16 +195,16 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit three
 	EOF
@@ -271,16 +271,16 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	HEAD is now at \$commit... three
 	EOF
-- 
2.21.0.rc1.337.gdf7f8d0522


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 00/21] Add new command "switch"
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (20 preceding siblings ...)
  2019-03-08  9:57     ` [PATCH v3 21/21] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-08 17:48     ` Ramsay Jones
  2019-03-09 11:56       ` Duy Nguyen
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
  22 siblings, 1 reply; 289+ messages in thread
From: Ramsay Jones @ 2019-03-08 17:48 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, gitster, sunshine, szeder.dev



On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
[snip]
> Range-diff dựa trên v2:
>  -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option
>  1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line
>  2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore
>     @@ -14,14 +14,15 @@
>       	out anyway. In other words, the ref can be held by more than one
>       	worktree.
>       
>     -+--[no-]overwrite-ignore::
>     ++--overwrite-ignore::
>     ++--no-overwrite-ignore::

Just curious, but why? Is '--[no-]overwrite-ignore' thought to
be harder to read? What about the rest of the man-pages?

ATB,
Ramsay Jones


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 00/21] Add new command "switch"
  2019-03-08 17:48     ` [PATCH v3 00/21] Add new command "switch" Ramsay Jones
@ 2019-03-09 11:56       ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-09 11:56 UTC (permalink / raw)
  To: Ramsay Jones
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Sat, Mar 9, 2019 at 12:49 AM Ramsay Jones
<ramsay@ramsayjones.plus.com> wrote:
>
>
>
> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> [snip]
> > Range-diff dựa trên v2:
> >  -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option
> >  1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line
> >  2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore
> >     @@ -14,14 +14,15 @@
> >               out anyway. In other words, the ref can be held by more than one
> >               worktree.
> >
> >     -+--[no-]overwrite-ignore::
> >     ++--overwrite-ignore::
> >     ++--no-overwrite-ignore::
>
> Just curious, but why? Is '--[no-]overwrite-ignore' thought to
> be harder to read? What about the rest of the man-pages?

It's also easier to search. If I remember correctly I did search for
the rest of man pages and the --no- wins over --[no-]
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset
  2019-03-08  9:57     ` [PATCH v3 04/21] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-09 12:35       ` Martin Ågren
  2019-03-11  9:35         ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Martin Ågren @ 2019-03-09 12:35 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Fri, 8 Mar 2019 at 10:58, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>  Documentation/git-checkout.txt | 60 +++++++++++++++++-----------------
>  1 file changed, 30 insertions(+), 30 deletions(-)
>
>  'git checkout' [<branch>]::
> -       To prepare for working on <branch>, switch to it by updating
> +       To prepare for working on `<branch>`, switch to it by updating
>         the index and the files in the working tree, and by pointing
>         HEAD at the branch. Local modifications to the files in the

You could wrap HEAD in backticks as well: `HEAD`.

> -       Prepare to work on top of <commit>, by detaching HEAD at it
> +       Prepare to work on top of `<commit>`, by detaching HEAD at it

Likewise.

>         (see "DETACHED HEAD" section), and updating the index and the

(but not here)

>  be used to detach HEAD at the tip of the branch (`git checkout
> -<branch>` would check out that branch without detaching HEAD).
> +`<branch>`` would check out that branch without detaching HEAD).

This results in a nesting. I would say this was already correct. (A few
more `HEAD` here.)

> -       Create a new branch named <new_branch> and start it at
> -       <start_point>; see linkgit:git-branch[1] for details.
> +       Create a new branch named `<new_branch>` and start it at
> +       `<start_point>`; see linkgit:git-branch[1] for details.

Lots of changes like this. Good.

>         This is the default behavior of "git checkout <commit>" when

Should be `git checkout <commit>`?

> -       <commit> is not a branch name.  See the "DETACHED HEAD" section
> +       `<commit>` is not a branch name.  See the "DETACHED HEAD" section

Good.

> -that records a set of paths similar to <start_point> by easily running
> +that records a set of paths similar to `<start_point>` by easily running
>  "git commit -a" to make the root commit.

Another opportunity for backticks.

I saw one change that I disagreed with and a few missed opportunities
(IMHO). How about something like the below squashed in? (This is based
on this point in the series.) It might be worth polishing this document
before copying it (or parts of it) in the next commit.

Martin

-- >8 --
Subject: git-checkout.txt: add more backticks for monospacing

Add backticks where we have none, replace single quotes with backticks
and replace double-quotes. Drop double-quotes from nested constructions
such as `"@{-1}"`. Add a missing possessive apostrophe after the word
"submodules" while at it.

Signed-off-by: Martin Ågren <martin.agren@gmail.com>
---
 Documentation/git-checkout.txt | 106 ++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 1b9d689933..af5906fbf5 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -26,7 +26,7 @@ branch.
 'git checkout' [<branch>]::
 	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
-	HEAD at the branch. Local modifications to the files in the
+	`HEAD` at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
 	`<branch>`.
 +
@@ -75,7 +75,7 @@ successful.
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of `<commit>`, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching `HEAD` at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
@@ -83,10 +83,10 @@ successful.
 	modifications.
 +
 When the `<commit>` argument is a branch name, the `--detach` option can
-be used to detach HEAD at the tip of the branch (`git checkout
-`<branch>`` would check out that branch without detaching HEAD).
+be used to detach `HEAD` at the tip of the branch (`git checkout
+<branch>` would check out that branch without detaching `HEAD`).
 +
-Omitting `<branch>` detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
@@ -128,7 +128,7 @@ OPTIONS
 -f::
 --force::
 	When switching branches, proceed even if the index or the
-	working tree differs from HEAD.  This is used to throw away
+	working tree differs from `HEAD`.  This is used to throw away
 	local changes.
 +
 When checking out paths from the index, do not fail upon unmerged
@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
 the initial part up to the "*".
-This would tell us to use "hack" as the local branch when branching
-off of "origin/hack" (or "remotes/origin/hack", or even
-"refs/remotes/origin/hack").  If the given name has no slash, or the above
+This would tell us to use `hack` as the local branch when branching
+off of `origin/hack` (or `remotes/origin/hack`, or even
+`refs/remotes/origin/hack`).  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with `-b` in such a case.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
-	branch.autoSetupMerge configuration variable is true.
+	`branch.autoSetupMerge` configuration variable is true.
 
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -190,7 +190,7 @@ explicitly give a name with `-b` in such a case.
 --detach::
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
-	This is the default behavior of "git checkout <commit>" when
+	This is the default behavior of `git checkout <commit>` when
 	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
@@ -202,9 +202,9 @@ explicitly give a name with `-b` in such a case.
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
-"git checkout <start_point>".  This allows you to start a new history
+`git checkout <start_point>`.  This allows you to start a new history
 that records a set of paths similar to `<start_point>` by easily running
-"git commit -a" to make the root commit.
+`git commit -a` to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
 without exposing its full history. You might want to do this to publish
@@ -215,14 +215,14 @@ code.
 If you want to start a disconnected history that records a set of paths
 that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
-branch by running "git rm -rf ." from the top level of the working tree.
+branch by running `git rm -rf .` from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
 	update only entries matched by `<paths>` and sparse patterns
-	in $GIT_DIR/info/sparse-checkout. This option ignores
+	in `$GIT_DIR/info/sparse-checkout`. This option ignores
 	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
@@ -245,9 +245,9 @@ When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
 
 --conflict=<style>::
-	The same as --merge option above, but changes the way the
+	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.conflictStyle` configuration variable.  Possible values are
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
@@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also
 	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
 	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	submodules' `HEAD`.
 
 --no-guess::
 	Do not attempt to create a branch if a remote tracking branch
@@ -303,14 +303,14 @@ Note that this option uses the no overlay mode by default (see also
 	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
 	branch is checked out. Otherwise, if it refers to a valid
-	commit, your HEAD becomes "detached" and you are no longer on
+	commit, your `HEAD` becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-You can use the `"@{-N}"` syntax to refer to the N-th last
+You can use the `@{-N}` syntax to refer to the N-th last
 branch/commit checked out using "git checkout" operation. You may
-also specify `-` which is synonymous to `"@{-1}"`.
+also specify `-` which is synonymous to `@{-1}`.
 +
-As a special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `A...B` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <start_point>::
 	The name of a commit at which to start the new branch; see
-	linkgit:git-branch[1] for details. Defaults to HEAD.
+	linkgit:git-branch[1] for details. Defaults to `HEAD`.
 
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 DETACHED HEAD
 -------------
-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
 branch refers to a specific commit. Let's look at a repo with three
-commits, one of them tagged, and with branch 'master' checked out:
+commits, one of them tagged, and with branch `master` checked out:
 
 ------------
            HEAD (refers to branch 'master')
@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')
 ------------
 
 When a commit is created in this state, the branch is updated to refer to
-the new commit. Specifically, 'git commit' creates a new commit 'd', whose
-parent is commit 'c', and then updates branch 'master' to refer to new
-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
-to commit 'd':
+the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+parent is commit `c`, and then updates branch `master` to refer to new
+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+to commit `d`:
 
 ------------
 $ edit; git add; git commit
@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 It is sometimes useful to be able to checkout a commit that is not at
 the tip of any named branch, or even to create a new commit that is not
 referenced by a named branch. Let's look at what happens when we
-checkout commit 'b' (here we show two ways this may be done):
+checkout commit `b` (here we show two ways this may be done):
 
 ------------
 $ git checkout v2.0  # or
@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-Notice that regardless of which checkout command we use, HEAD now refers
-directly to commit 'b'. This is known as being in detached HEAD state.
-It means simply that HEAD refers to a specific commit, as opposed to
+Notice that regardless of which checkout command we use, `HEAD` now refers
+directly to commit `b`. This is known as being in detached `HEAD` state.
+It means simply that `HEAD` refers to a specific commit, as opposed to
 referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-There is now a new commit 'e', but it is referenced only by HEAD. We can
+There is now a new commit `e`, but it is referenced only by `HEAD`. We can
 of course add yet another commit in this state:
 
 ------------
@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 In fact, we can perform all the normal Git operations. But, let's look
-at what happens when we then checkout master:
+at what happens when we then checkout `master`:
 
 ------------
 $ git checkout master
@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 It is important to realize that at this point nothing refers to commit
-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
 by the routine Git garbage collection process, unless we create a reference
-before that happens. If we have not yet moved away from commit 'f',
+before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
@@ -442,19 +442,19 @@ $ git branch foo        <2>
 $ git tag foo           <3>
 ------------
 
-<1> creates a new branch 'foo', which refers to commit 'f', and then
-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-    be in detached HEAD state after this command.
+<1> creates a new branch `foo`, which refers to commit `f`, and then
+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+    be in detached `HEAD` state after this command.
 
-<2> similarly creates a new branch 'foo', which refers to commit 'f',
-    but leaves HEAD detached.
+<2> similarly creates a new branch `foo`, which refers to commit `f`,
+    but leaves `HEAD` detached.
 
-<3> creates a new tag 'foo', which refers to commit 'f',
-    leaving HEAD detached.
+<3> creates a new tag `foo`, which refers to commit `f`,
+    leaving `HEAD` detached.
 
-If we have moved away from commit 'f', then we must first recover its object
+If we have moved away from commit `f`, then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
-it. For example, to see the last two commits to which HEAD referred, we
+it. For example, to see the last two commits to which `HEAD` referred, we
 can use either of these commands:
 
 ------------
@@ -465,12 +465,12 @@ $ git log -g -2 HEAD
 ARGUMENT DISAMBIGUATION
 -----------------------
 
-When there is only one argument given and it is not `--` (e.g. "git
-checkout abc"), and when the argument is both a valid `<tree-ish>`
-(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+When there is only one argument given and it is not `--` (e.g. `git
+checkout abc`), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
 or a directory whose name is "abc" exists), Git would usually ask
 you to disambiguate.  Because checking out a branch is so common an
-operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
 in such a situation.  Use `git checkout -- <pathspec>` if you want
 to checkout these paths out of the index.
 
@@ -478,7 +478,7 @@ EXAMPLES
 --------
 
 . The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes hello.c by
+  the `Makefile` to two revisions back, deletes `hello.c` by
   mistake, and gets it back from the index.
 +
 ------------
@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>
 +
 <1> switch branch
 <2> take a file out of another commit
-<3> restore hello.c from the index
+<3> restore `hello.c` from the index
 +
 If you want to check out _all_ C source files out of the index,
 you can say
@@ -519,7 +519,7 @@ $ git checkout -- hello.c
 $ git checkout mytopic
 ------------
 +
-However, your "wrong" branch and correct "mytopic" branch may
+However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-09 12:36       ` Martin Ågren
  2019-03-10 22:57       ` Jacob Keller
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 289+ messages in thread
From: Martin Ågren @ 2019-03-09 12:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Fri, 8 Mar 2019 at 11:00, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> ---

(Missing signed-off-by.)

> --- /dev/null
> +++ b/Documentation/git-switch.txt
> @@ -0,0 +1,259 @@

It looks like you base this on git-checkout.txt, which makes sense.

> +--recurse-submodules::
> +--no-recurse-submodules::
> +       Using --recurse-submodules will update the content of all initialized

But it's based on an older copy. You add backticks here in patch 4.

Martin

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 01/21] git-checkout.txt: spell out --no-option
  2019-03-08  9:57     ` [PATCH v3 01/21] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
@ 2019-03-10  3:28       ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-10  3:28 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, SZEDER Gábor

On Fri, Mar 8, 2019 at 4:58 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> It's easier to search for and also less cryptic.
> ---

Your sign-off is missing from the entire series.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 18/21] switch: no worktree status unless real branch switch happens
  2019-03-08  9:57     ` [PATCH v3 18/21] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-03-10  3:43       ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-10  3:43 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, SZEDER Gábor

On Fri, Mar 8, 2019 at 4:59 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> When we switch from one branch to another, it makes sense to show a
> summary of local changes since there could be conflicts, or some files
> left modified.... When switch is used solely for creating a new
> branch (and "switch" to the same commit) or detaching, we don't really
> need to show anything.
>
> "git checkout" does it anyway for historical reasons. But we can start
> with a clean slate with switch and don't have to.
>
> This essentially reverts fa655d8411 (checkout: optimize "git checkout
> -b <new_branch>" - 2018-08-16) and make it default for switch,
> but also for -B and --detach. Users of big repos are encouraged to
> move to switch.

I like this last bit. The skip_merge_working_tree() function which
this removes was ugly, difficult to maintain, and difficult to get
just right (and easy to break -- even by changing parts of the system
which one might not expect to impact it).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 19/21] t: add tests for switch
  2019-03-08  9:57     ` [PATCH v3 19/21] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-03-10  3:59       ` Eric Sunshine
  2019-03-10 10:09       ` Andrei Rybak
  1 sibling, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-10  3:59 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, SZEDER Gábor

On Fri, Mar 8, 2019 at 4:59 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
> @@ -0,0 +1,87 @@
> +test_expect_success 'switch to a commit' '
> +       test_must_fail git switch master^{commit}
> +'

The title of this test was a bit misleading for me; I would have found
it easier to understand what's being tested had it said "switch
without --detach" or something.

> +test_expect_success 'switch and detach' '
> +       test_when_finished git switch master &&
> +       git switch --detach master^{commit} &&
> +       test_must_fail git symbolic-ref HEAD
> +'

In fact, if the two tests were combined, it would have been even
clearer (for me):

    test_expect_success 'switch and detach' '
        test_when_finished git switch master &&
        test_must_fail git switch master^{commit} &&
        git switch --detach master^{commit} &&
        test_must_fail git symbolic-ref HEAD
    '

Not worth a re-roll.

> +test_expect_success 'switch and create branch' '
> +       test_when_finished git switch master &&
> +       git switch -c temp master^ &&
> +       test_cmp_rev master^ refs/heads/temp &&
> +       echo refs/heads/temp >expected-branch &&
> +       git symbolic-ref HEAD >actual-branch &&
> +       test_cmp expected-branch actual-branch
> +'
> +
> +test_expect_success 'force create branch from HEAD' '
> +       test_when_finished git switch master &&
> +       git switch --detach master &&
> +       git switch -C temp &&
> +       test_cmp_rev master refs/heads/temp &&
> +       echo refs/heads/temp >expected-branch &&
> +       git symbolic-ref HEAD >actual-branch &&
> +       test_cmp expected-branch actual-branch
> +'

Maybe also demonstrate that -C is actually needed here by leading in
with a failing -c:

    ...
    git switch --detach master &&
    test_must_fail git switch -c temp &&
    git switch -C temp &&
    ...

Not worth a re-roll.

> +test_expect_success 'guess and create branch ' '
> +       test_when_finished git switch master &&
> +       test_must_fail git switch foo &&
> +       git switch --guess foo &&
> +       echo refs/heads/foo >expected &&
> +       git symbolic-ref HEAD >actual &&
> +       test_cmp expected actual
> +'

The above suggestions about --detach/-C reflect how you did it in this
test, in which you first try "git switch foo" without the --guess
option, expecting it to fail, and then repeat with the option,
expecting it to succeed.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 19/21] t: add tests for switch
  2019-03-08  9:57     ` [PATCH v3 19/21] t: add tests for switch Nguyễn Thái Ngọc Duy
  2019-03-10  3:59       ` Eric Sunshine
@ 2019-03-10 10:09       ` Andrei Rybak
  1 sibling, 0 replies; 289+ messages in thread
From: Andrei Rybak @ 2019-03-10 10:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, gitster, sunshine, szeder.dev

On 3/8/19 10:57 AM, Nguyễn Thái Ngọc Duy wrote:
> ---
>  t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 87 insertions(+)
>  create mode 100755 t/t2060-switch.sh
> 
> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
> new file mode 100755
> index 0000000000..1e1e834c1b
> --- /dev/null
> +++ b/t/t2060-switch.sh
> @@ -0,0 +1,87 @@
> +#!/bin/sh
> +

[snip]

> +
> +test_expect_success 'switching ignores file of same branch name' '
> +	test_when_finished git switch master &&
> +	: >first-branch &&
> +	git switch first-branch &&
> +	echo refs/heads/first-branch >expected &&
> +	git symbolic-ref HEAD >actual &&
> +	test_commit expected actual

s/commit/cmp/

> +'
> +
> +test_expect_success 'guess and create branch ' '
> +	test_when_finished git switch master &&
> +	test_must_fail git switch foo &&
> +	git switch --guess foo &&
> +	echo refs/heads/foo >expected &&
> +	git symbolic-ref HEAD >actual &&
> +	test_cmp expected actual
> +'
> +
> +test_done
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
  2019-03-09 12:36       ` Martin Ågren
@ 2019-03-10 22:57       ` Jacob Keller
  2019-03-11 19:00         ` Elijah Newren
  2019-03-11 11:16       ` Phillip Wood
  2019-03-11 17:54       ` Elijah Newren
  3 siblings, 1 reply; 289+ messages in thread
From: Jacob Keller @ 2019-03-10 22:57 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, pclouds
  Cc: git, gitster, sunshine, szeder.dev



On March 8, 2019 1:57:41 AM PST, "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com> wrote:
>"git checkout" doing too many things is a source of confusion for many
>users (and it even bites old timers sometimes). To remedy that, the
>command will be split into two new ones: switch and
>something-to-checkout-paths. The good old "git checkout" command is
>still here and will be until all (or most of users) are sick of it.
>
>See the new man page for the final design of switch. The actual
>implementation though is still pretty much the same as "git checkout"
>and not completely aligned with the man page. Following patches will
>adjust their behavior to match the man page.
>---
> .gitignore                        |   1 +
> Documentation/config/advice.txt   |  13 +-
> Documentation/config/branch.txt   |   4 +-
> Documentation/config/checkout.txt |   9 +-
> Documentation/config/diff.txt     |   3 +-
> Documentation/git-checkout.txt    |   4 +
> Documentation/git-switch.txt      | 259 ++++++

>+<new-branch>::
>+	Name for the new branch.
>+
>+<start-point>::
>+	The name of a commit at which to switch to before creating a
>+	new branch or detach from.

The wording here (and a few other places) feels awkward to me. I don't really have a better wording but maybe:

---
The name of the commit to switch to when creating a new branch or detaching HEAD
---

The original has weird tense when using detach.

There were a few other places like this where the wording was "or detach from" but where the verb tense was confusing

Thanks,
Jake
-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset
  2019-03-09 12:35       ` Martin Ågren
@ 2019-03-11  9:35         ` Duy Nguyen
  2019-03-11 10:41           ` Martin Ågren
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-11  9:35 UTC (permalink / raw)
  To: Martin Ågren
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Sat, Mar 9, 2019 at 7:35 PM Martin Ågren <martin.agren@gmail.com> wrote:

> @@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also
The part not shown here is

    Using `--recurse-submodules` will update the content of all initialized
    submodules according to the commit recorded in the superproject. If
    local modifications in a submodule would be overwritten the checkout

and the --recurse-submodules is rendered incorrectly (not with
monospace font, and the quotes remain) because...

>         will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
>         is used, the work trees of submodules will not be updated.
>         Just like linkgit:git-submodule[1], this will detach the
> -       submodules HEAD.
> +       submodules' `HEAD`.

...of this apostrophe, it seems, on both man and html versions. This
is with asciidoc 8.6.9.

Martin, could you check if your asciidoc (or asciidoctor) behaves the
same? If it's not just my buggy asciidoc version, I can turn this to
"wil detach `HEAD` of the submodule" which should fix the problem.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset
  2019-03-11  9:35         ` Duy Nguyen
@ 2019-03-11 10:41           ` Martin Ågren
  0 siblings, 0 replies; 289+ messages in thread
From: Martin Ågren @ 2019-03-11 10:41 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Mon, 11 Mar 2019 at 10:35, Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Sat, Mar 9, 2019 at 7:35 PM Martin Ågren <martin.agren@gmail.com> wrote:
>
> > @@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also
> The part not shown here is
>
>     Using `--recurse-submodules` will update the content of all initialized
>     submodules according to the commit recorded in the superproject. If
>     local modifications in a submodule would be overwritten the checkout
>
> and the --recurse-submodules is rendered incorrectly (not with
> monospace font, and the quotes remain) because...
>
> >         will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
> >         is used, the work trees of submodules will not be updated.
> >         Just like linkgit:git-submodule[1], this will detach the
> > -       submodules HEAD.
> > +       submodules' `HEAD`.
>
> ...of this apostrophe, it seems, on both man and html versions. This
> is with asciidoc 8.6.9.

Oh wow, nicely debugged. Yeah, I see this with 8.6.10, but not with
Asciidoctor (1.5.5).

> Martin, could you check if your asciidoc (or asciidoctor) behaves the
> same? If it's not just my buggy asciidoc version, I can turn this to
> "wil detach `HEAD` of the submodule" which should fix the problem.

Sounds good.

Martin

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
  2019-03-09 12:36       ` Martin Ågren
  2019-03-10 22:57       ` Jacob Keller
@ 2019-03-11 11:16       ` Phillip Wood
  2019-03-11 11:47         ` Duy Nguyen
                           ` (5 more replies)
  2019-03-11 17:54       ` Elijah Newren
  3 siblings, 6 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-11 11:16 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, gitster, sunshine, szeder.dev, Martin Ågren


Hi Duy

On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> "git checkout" doing too many things is a source of confusion for many
> users (and it even bites old timers sometimes). To remedy that, the
> command will be split into two new ones: switch and
> something-to-checkout-paths. 

I think this is a good idea, thanks for working on it. I wonder if it
would be a good idea to have the new command refuse to checkout a new
branch if there is a cherry-pick/revert/merge/rebase in progress (with
an option to override the check) as switching branches in the middle of
one of those is likely to be confusing to users (if I do it it is
normally because I've forgotten that I've not run 'git whatever
--continue').

> The good old "git checkout" command is
> still here and will be until all (or most of users) are sick of it.
> 
> See the new man page for the final design of switch. The actual
> implementation though is still pretty much the same as "git checkout"
> and not completely aligned with the man page. Following patches will
> adjust their behavior to match the man page.
> ---
>  .gitignore                        |   1 +
>  Documentation/config/advice.txt   |  13 +-
>  Documentation/config/branch.txt   |   4 +-
>  Documentation/config/checkout.txt |   9 +-
>  Documentation/config/diff.txt     |   3 +-
>  Documentation/git-checkout.txt    |   4 +
>  Documentation/git-switch.txt      | 259 ++++++++++++++++++++++++++++++
>  Documentation/gitattributes.txt   |   3 +-
>  Documentation/githooks.txt        |   8 +-
>  Makefile                          |   1 +
>  builtin.h                         |   1 +
>  builtin/checkout.c                |  60 +++++--
>  command-list.txt                  |   1 +
>  git.c                             |   1 +
>  14 files changed, 341 insertions(+), 27 deletions(-)
>  create mode 100644 Documentation/git-switch.txt
> 
> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
> new file mode 100644
> index 0000000000..ff60ba69fb
> --- /dev/null
> +++ b/Documentation/git-switch.txt
> @@ -0,0 +1,259 @@
> +git-switch(1)
> +=============
> +
> +NAME
> +----
> +git-switch - Switch branches
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'git switch' [<options>] [--guess] <branch>
> +'git switch' [<options>] --detach [<start-point>]
> +'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
> +
> +DESCRIPTION
> +-----------
> +Switch to a specified branch. The working tree and the index are
> +updated to match the branch. All new commits will be added to the tip
> +of this branch.
> +
> +Optionally a new branch could be created with either `-c` or `-C`, or
> +detach the working tree from any branch with `--detach`, along with
> +switching.
> +
> +Switching branches does not require a clean index and working tree
> +(i.e. no differences compared to 'HEAD'). The operation is aborted
> +however if the switch leads to loss of local changes, unless told
> +otherwise.
> +
> +OPTIONS
> +-------
> +<branch>::
> +	Branch to switch to.
> +
> +<new-branch>::
> +	Name for the new branch.
> +
> +<start-point>::
> +	The name of a commit at which to switch to before creating a
> +	new branch or detach from.
> ++
> +You can use the `"@{-N}"` syntax to refer to the N-th last
> +branch/commit switched to "git switch" or "git checkout"
> +operation. You may also specify `-` which is synonymous to `"@{-1}"`.
> ++
> +As a special case, you may use `"A...B"` as a shortcut for the merge
> +base of `A` and `B` if there is exactly one merge base. You can leave
> +out at most one of `A` and `B`, in which case it defaults to `HEAD`.
> +
> +-c <new-branch>::
> +--create <new-branch>::
> +	Create a new branch named `<new-branch>` starting at
> +	`<start-point>` before switching to the branch. This is a
> +	convenient shortcut for:
> ++
> +------------
> +$ git branch <new-branch>
> +$ git switch <new-branch>
> +------------
> +
> +-C <new-branch>::
> +--force-create <new-branch>::
> +	Similar to `--create` except that if `<new-branch>` already
> +	exists, it will be reset to `<start-point>`. This is a
> +	convenient shortcut for:

If we're renaming the options to be more meaningful then maybe we should
consider a different name for this one - it has nothing to do with
creating a branch. Maybe --reset or --update?

> ++
> +------------
> +$ git branch -f <new-branch>
> +$ git switch <new-branch>
> +------------
> +
> +-d::
> +--detach::
> +	Switch to a commit for inspection and discardable
> +	experiments. See the "DETACHED HEAD" section in
> +	linkgit:git-checkout[1] for details.
> +
> +-g::
> +--guess::
> +	If `<branch>` is not found but there does exist a tracking
> +	branch in exactly one remote (call it `<remote>`) with a
> +	matching name, treat as equivalent to
> ++
> +------------
> +$ git switch -c <branch> --track <remote>/<branch>
> +------------
> ++
> +If the branch exists in multiple remotes and one of them is named by
> +the `checkout.defaultRemote` configuration variable, we'll use that
> +one for the purposes of disambiguation, even if the `<branch>` isn't
> +unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
> +to always checkout remote branches from there if `<branch>` is
> +ambiguous but exists on the 'origin' remote. See also
> +`checkout.defaultRemote` in linkgit:git-config[1].
> +
> +-q::
> +--quiet::
> +	Quiet, suppress feedback messages.
> +
> +-f::
> +--force::
> +	Proceed even if the index or the working tree differs from
> +	HEAD. Both the index and working tree are restored to match
> +	the switching target. This is used to throw away local
> +	changes.

I'd always thought that --force meant "throw away my local changes if
they conflict with the new branch" not "throw them away regardless"
(which is better as it is deterministic). Maybe we can come up with a
clearer name here --discard-changes? At the moment --force does not
throw away conflicts properly (see the script below in my comments about
--merge).

> +
> +--progress::
> +--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`.
> +
> +-t::
> +--track::
> +	When creating a new branch, set up "upstream" configuration.
> +	`-c` is implied. See "--track" in linkgit:git-branch[1] for
> +	details.
> ++
> +If no `-c` option is given, the name of the new branch will be derived
> +from the remote-tracking branch, by looking at the local part of the
> +refspec configured for the corresponding remote, and then stripping
> +the initial part up to the "*".  This would tell us to use "hack" as
> +the local branch when branching off of "origin/hack" (or
> +"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
> +given name has no slash, or the above guessing results in an empty
> +name, the guessing is aborted.  You can explicitly give a name with
> +`-c` in such a case.
> +
> +--no-track::
> +	Do not set up "upstream" configuration, even if the
> +	branch.autoSetupMerge configuration variable is true.
> +
> +-m::
> +--merge::
> +	If you have local modifications to one or more files that are
> +	different between the current branch and the branch to which
> +	you are switching, the command refuses to switch branches in
> +	order to preserve your modifications in context.  However,
> +	with this option, a three-way merge between the current
> +	branch, your working tree contents, and the new branch is
> +	done, and you will be on the new branch.

I was wondering what people felt about making this the default for the
new command. If I'm carrying changes over to the new branch then I want
them to be merged in, it's annoying to have them carried over most of
the time but then sometimes have the checkout fail without specifying
--merge. At the moment if --merge falls back to doing a three-way merge
then it loses any newly staged files that are not in either HEAD. I
think they are thrown away in reset_tree(). Here is a script to
demonstrate that

d="$(mktemp -d)" &&
cd "$d" &&
git init &&
echo a>a &&
git add a &&
git commit -ama &&
git branch topic &&
echo b>a &&
git commit -amb &&
git checkout topic &&
echo c>a &&
echo x>x &&
git add x &&
git checkout -m master &&
echo 'x is missing &&
! git ls-files --error-unmatch x &&
# a is unmerged
git checkout -f topic &&
echo 'a is in stage 1'
git ls-files -s a


Best Wishes

Phillip

> ++
> +When a merge conflict happens, the index entries for conflicting
> +paths are left unmerged, and you need to resolve the conflicts
> +and mark the resolved paths with `git add` (or `git rm` if the merge
> +should result in deletion of the path).
> +
> +--conflict=<style>::
> +	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).
> +
> +--orphan <new-branch>::
> +	Create a new 'orphan' branch, named `<new-branch>`, started from
> +	`<start-point>` and switch to it. See explanation of the same
> +	option in linkgit:git-checkout[1] for details.
> +
> +--ignore-other-worktrees::
> +	`git switch` refuses when the wanted ref is already
> +	checked out by another worktree. This option makes it check
> +	the ref out anyway. In other words, the ref can be held by
> +	more than one worktree.
> +
> +--recurse-submodules::
> +--no-recurse-submodules::
> +	Using --recurse-submodules will update the content of all initialized
> +	submodules according to the commit recorded in the superproject. If
> +	local modifications in a submodule would be overwritten the checkout
> +	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
> +	is used, the work trees of submodules will not be updated.
> +	Just like linkgit:git-submodule[1], this will detach the
> +	submodules HEAD.
> +
> +EXAMPLES
> +--------
> +
> +The following command switches to the "master" branch:
> +
> +------------
> +$ git switch master
> +------------
> +
> +After working in the wrong branch, switching to the correct branch
> +would be done using:
> +
> +------------
> +$ git switch mytopic
> +------------
> +
> +However, your "wrong" branch and correct "mytopic" branch may differ
> +in files that you have modified locally, in which case the above
> +switch would fail like this:
> +
> +------------
> +$ git switch mytopic
> +error: You have local changes to 'frotz'; not switching branches.
> +------------
> +
> +You can give the `-m` flag to the command, which would try a three-way
> +merge:
> +
> +------------
> +$ git switch -m mytopic
> +Auto-merging frotz
> +------------
> +
> +After this three-way merge, the local modifications are _not_
> +registered in your index file, so `git diff` would show you what
> +changes you made since the tip of the new branch.
> +
> +To switch back to the previous branch before we switched to mytopic
> +(i.e. "master" branch):
> +
> +------------
> +$ git switch -
> +------------
> +
> +You can grow a new branch from any commit. For example, switch to
> +"HEAD~3" and create branch "fixup":
> +
> +------------
> +$ git switch -c fixup HEAD~3
> +Switched to a new branch 'fixup'
> +------------
> +
> +If you want to start a new branch from a remote branch of the same
> +name, use `-g`:
> +
> +------------
> +$ git switch -g new-topic
> +Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
> +Switched to a new branch 'new-topic'
> +------------
> +
> +To check out commit "HEAD~3" for temporary inspection or experiment
> +without creating a new branch:
> +
> +------------
> +$ git switch --detach HEAD~3
> +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
> +------------
> +
> +If it turns out whatever you have done is worth keeping, you can
> +always create a new name for it (without switching away):
> +
> +------------
> +$ git switch -c good-surprises
> +------------
> +
> +SEE ALSO
> +--------
> +linkgit:git-checkout[1]
> +
> +GIT
> +---
> +Part of the linkgit:git[1] suite
> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
> index 9b41f81c06..cd0f9fa507 100644
> --- a/Documentation/gitattributes.txt
> +++ b/Documentation/gitattributes.txt
> @@ -112,7 +112,8 @@ Checking-out and checking-in
>  
>  These attributes affect how the contents stored in the
>  repository are copied to the working tree files when commands
> -such as 'git checkout' and 'git merge' run.  They also affect how
> +such as 'git switch', 'git checkout'  and 'git merge' run.
> +They also affect how
>  Git stores the contents you prepare in the working tree in the
>  repository upon 'git add' and 'git commit'.
>  
> diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
> index 5bf653c111..8ff72f0613 100644
> --- a/Documentation/githooks.txt
> +++ b/Documentation/githooks.txt
> @@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
>  post-checkout
>  ~~~~~~~~~~~~~
>  
> -This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
> +This hook is invoked when a linkgit:git-checkout[1] or
> +linkgit:git-switch[1] is run after having updated the
>  worktree.  The hook is given three parameters: the ref of the previous HEAD,
>  the ref of the new HEAD (which may or may not have changed), and a flag
>  indicating whether the checkout was a branch checkout (changing branches,
>  flag=1) or a file checkout (retrieving a file from the index, flag=0).
> -This hook cannot affect the outcome of `git checkout`.
> +This hook cannot affect the outcome of `git switch` or `git checkout`.
>  
>  It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
>  used. The first parameter given to the hook is the null-ref, the second the
> @@ -406,7 +407,8 @@ exit with a zero status.
>  For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
>  in order to emulate `git fetch` that is run in the reverse direction
>  with `git push`, as the two-tree form of `git read-tree -u -m` is
> -essentially the same as `git checkout` that switches branches while
> +essentially the same as `git switch` or `git checkout`
> +that switches branches while
>  keeping the local changes in the working tree that do not interfere
>  with the difference between the branches.
>  
> diff --git a/Makefile b/Makefile
> index 148668368b..8e91db73ad 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X
>  BUILT_INS += git-show$X
>  BUILT_INS += git-stage$X
>  BUILT_INS += git-status$X
> +BUILT_INS += git-switch$X
>  BUILT_INS += git-whatchanged$X
>  
>  # what 'all' will build and 'install' will install in gitexecdir,
> diff --git a/builtin.h b/builtin.h
> index 6538932e99..c64e44450e 100644
> --- a/builtin.h
> +++ b/builtin.h
> @@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
>  extern int cmd_status(int argc, const char **argv, const char *prefix);
>  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
>  extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
> +extern int cmd_switch(int argc, const char **argv, const char *prefix);
>  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
>  extern int cmd_tag(int argc, const char **argv, const char *prefix);
>  extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 7d23083282..1eff10dbef 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {
>  	NULL,
>  };
>  
> +static const char * const switch_branch_usage[] = {
> +	N_("git switch [<options>] [<branch>]"),
> +	NULL,
> +};
> +
>  struct checkout_opts {
>  	int patch_mode;
>  	int quiet;
> @@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
>  	return newopts;
>  }
>  
> -int cmd_checkout(int argc, const char **argv, const char *prefix)
> +static int checkout_main(int argc, const char **argv, const char *prefix,
> +			 struct checkout_opts *opts, struct option *options,
> +			 const char * const usagestr[])
>  {
> -	struct checkout_opts real_opts;
> -	struct checkout_opts *opts = &real_opts;
>  	struct branch_info new_branch_info;
>  	int dwim_remotes_matched = 0;
>  	int dwim_new_local_branch;
> -	struct option *options = NULL;
>  
> -	memset(opts, 0, sizeof(*opts));
>  	memset(&new_branch_info, 0, sizeof(new_branch_info));
>  	opts->overwrite_ignore = 1;
>  	opts->prefix = prefix;
>  	opts->show_progress = -1;
>  	opts->overlay_mode = -1;
> -	opts->no_dwim_new_local_branch = 0;
>  
>  	git_config(git_checkout_config, opts);
>  
>  	opts->track = BRANCH_TRACK_UNSPECIFIED;
>  
> -	options = parse_options_dup(options);
> -	options = add_common_options(opts, options);
> -	options = add_switch_branch_options(opts, options);
> -	options = add_checkout_path_options(opts, options);
> -
> -	argc = parse_options(argc, argv, prefix, options, checkout_usage,
> +	argc = parse_options(argc, argv, prefix, options, usagestr,
>  			     PARSE_OPT_KEEP_DASHDASH);
>  
>  	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
> @@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>  		return checkout_branch(opts, &new_branch_info);
>  	}
>  }
> +
> +int cmd_checkout(int argc, const char **argv, const char *prefix)
> +{
> +	struct checkout_opts opts;
> +	struct option *options = NULL;
> +	int ret;
> +
> +	memset(&opts, 0, sizeof(opts));
> +	opts.no_dwim_new_local_branch = 0;
> +
> +	options = parse_options_dup(options);
> +	options = add_common_options(&opts, options);
> +	options = add_switch_branch_options(&opts, options);
> +	options = add_checkout_path_options(&opts, options);
> +
> +	ret = checkout_main(argc, argv, prefix, &opts,
> +			    options, checkout_usage);
> +	FREE_AND_NULL(options);
> +	return ret;
> +}
> +
> +int cmd_switch(int argc, const char **argv, const char *prefix)
> +{
> +	struct checkout_opts opts;
> +	struct option *options = NULL;
> +	int ret;
> +
> +	memset(&opts, 0, sizeof(opts));
> +	opts.no_dwim_new_local_branch = 0;
> +
> +	options = parse_options_dup(options);
> +	options = add_common_options(&opts, options);
> +	options = add_switch_branch_options(&opts, options);
> +
> +	ret = checkout_main(argc, argv, prefix, &opts,
> +			    options, switch_branch_usage);
> +	FREE_AND_NULL(options);
> +	return ret;
> +}
> diff --git a/command-list.txt b/command-list.txt
> index 3a9af104b5..13317f47d4 100644
> --- a/command-list.txt
> +++ b/command-list.txt
> @@ -171,6 +171,7 @@ git-status                              mainporcelain           info
>  git-stripspace                          purehelpers
>  git-submodule                           mainporcelain
>  git-svn                                 foreignscminterface
> +git-switch                              mainporcelain           history
>  git-symbolic-ref                        plumbingmanipulators
>  git-tag                                 mainporcelain           history
>  git-unpack-file                         plumbinginterrogators
> diff --git a/git.c b/git.c
> index 2014aab6b8..39582cf511 100644
> --- a/git.c
> +++ b/git.c
> @@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
>  	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
>  	{ "stripspace", cmd_stripspace },
>  	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
> +	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
>  	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
>  	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
>  	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:16       ` Phillip Wood
@ 2019-03-11 11:47         ` Duy Nguyen
  2019-03-11 17:03           ` Phillip Wood
  2019-03-11 17:24           ` Elijah Newren
  2019-03-14  3:29         ` Duy Nguyen
                           ` (4 subsequent siblings)
  5 siblings, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-11 11:47 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren, Elijah Newren

On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
>
> Hi Duy
>
> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > "git checkout" doing too many things is a source of confusion for many
> > users (and it even bites old timers sometimes). To remedy that, the
> > command will be split into two new ones: switch and
> > something-to-checkout-paths.
>
> I think this is a good idea, thanks for working on it. I wonder if it
> would be a good idea to have the new command refuse to checkout a new
> branch if there is a cherry-pick/revert/merge/rebase in progress (with
> an option to override the check) as switching branches in the middle of
> one of those is likely to be confusing to users (if I do it it is
> normally because I've forgotten that I've not run 'git whatever
> --continue').

Interesting. I think this would be a good default if we have an escape
hatch (which could even come later). I often wander off to some other
branch and go back. But then half the time I end up forgetting I'm in
a middle of something and just "git rebase --quit" :P

Of course with git-stash (*) and git-worktree, I guess there's little
reason to just switch away from a something-in-progress worktree. I'll
try to implement this in the next round, unless someone objects.

(*) I hope git-stash remembers and restores "something-in-progress"
status. Dunno. Never used it much.

> > +-C <new-branch>::
> > +--force-create <new-branch>::
> > +     Similar to `--create` except that if `<new-branch>` already
> > +     exists, it will be reset to `<start-point>`. This is a
> > +     convenient shortcut for:
>
> If we're renaming the options to be more meaningful then maybe we should
> consider a different name for this one - it has nothing to do with
> creating a branch. Maybe --reset or --update?

-C can also create a new branch like -c though. --reset or --update
does not convey that (and --update sounds a bit too safe). Another
option is --recreate.

> > +-f::
> > +--force::
> > +     Proceed even if the index or the working tree differs from
> > +     HEAD. Both the index and working tree are restored to match
> > +     the switching target. This is used to throw away local
> > +     changes.
>
> I'd always thought that --force meant "throw away my local changes if
> they conflict with the new branch" not "throw them away regardless"
> (which is better as it is deterministic). Maybe we can come up with a
> clearer name here --discard-changes? At the moment --force does not
> throw away conflicts properly (see the script below in my comments about
> --merge).

Yeah if you want to redefine --force, now is a very good time.
Personally I'd rather have separate options than the "one catch all"
--force (or worse, multiple of --force). I'll leave this for the
community to decide.

Adding Elijah too. He also had some concern about "git restore
--force". Maybe he's interested in this as well.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:47         ` Duy Nguyen
@ 2019-03-11 17:03           ` Phillip Wood
  2019-03-12 11:54             ` Duy Nguyen
  2019-03-11 17:24           ` Elijah Newren
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-11 17:03 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren, Elijah Newren

Hi Duy

On 11/03/2019 11:47, Duy Nguyen wrote:
> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>>
>> Hi Duy
>>
>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
>>> "git checkout" doing too many things is a source of confusion for many
>>> users (and it even bites old timers sometimes). To remedy that, the
>>> command will be split into two new ones: switch and
>>> something-to-checkout-paths.
>>
>> I think this is a good idea, thanks for working on it. I wonder if it
>> would be a good idea to have the new command refuse to checkout a new
>> branch if there is a cherry-pick/revert/merge/rebase in progress (with
>> an option to override the check) as switching branches in the middle of
>> one of those is likely to be confusing to users (if I do it it is
>> normally because I've forgotten that I've not run 'git whatever
>> --continue').
> 
> Interesting. I think this would be a good default if we have an escape
> hatch (which could even come later). I often wander off to some other
> branch and go back. But then half the time I end up forgetting I'm in
> a middle of something and just "git rebase --quit" :P
> 
> Of course with git-stash (*) and git-worktree, I guess there's little
> reason to just switch away from a something-in-progress worktree. I'll
> try to implement this in the next round, unless someone objects.
> 
> (*) I hope git-stash remembers and restores "something-in-progress"
> status. Dunno. Never used it much.

I don't think it does. For rebase it's non trivial as it needs to 
remember the refs under refs/rewritten and stop gc from collecting any 
of them or the original head (in theory the todo list can contain 
commits that the user has added from other branches as well so they'd 
also need to be protected from gc). For cherry-pick there are gc issues 
as well.

Best Wishes

Phillip

> 
>>> +-C <new-branch>::
>>> +--force-create <new-branch>::
>>> +     Similar to `--create` except that if `<new-branch>` already
>>> +     exists, it will be reset to `<start-point>`. This is a
>>> +     convenient shortcut for:
>>
>> If we're renaming the options to be more meaningful then maybe we should
>> consider a different name for this one - it has nothing to do with
>> creating a branch. Maybe --reset or --update?
> 
> -C can also create a new branch like -c though. --reset or --update
> does not convey that (and --update sounds a bit too safe). Another
> option is --recreate.
> 
>>> +-f::
>>> +--force::
>>> +     Proceed even if the index or the working tree differs from
>>> +     HEAD. Both the index and working tree are restored to match
>>> +     the switching target. This is used to throw away local
>>> +     changes.
>>
>> I'd always thought that --force meant "throw away my local changes if
>> they conflict with the new branch" not "throw them away regardless"
>> (which is better as it is deterministic). Maybe we can come up with a
>> clearer name here --discard-changes? At the moment --force does not
>> throw away conflicts properly (see the script below in my comments about
>> --merge).
> 
> Yeah if you want to redefine --force, now is a very good time.
> Personally I'd rather have separate options than the "one catch all"
> --force (or worse, multiple of --force). I'll leave this for the
> community to decide.
> 
> Adding Elijah too. He also had some concern about "git restore
> --force". Maybe he's interested in this as well.
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:47         ` Duy Nguyen
  2019-03-11 17:03           ` Phillip Wood
@ 2019-03-11 17:24           ` Elijah Newren
  2019-03-11 20:51             ` Phillip Wood
  2019-03-12 11:58             ` Duy Nguyen
  1 sibling, 2 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-11 17:24 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >
> > Hi Duy
> >
> > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > > "git checkout" doing too many things is a source of confusion for many
> > > users (and it even bites old timers sometimes). To remedy that, the
> > > command will be split into two new ones: switch and
> > > something-to-checkout-paths.
> >
> > I think this is a good idea, thanks for working on it. I wonder if it
> > would be a good idea to have the new command refuse to checkout a new
> > branch if there is a cherry-pick/revert/merge/rebase in progress (with
> > an option to override the check) as switching branches in the middle of
> > one of those is likely to be confusing to users (if I do it it is
> > normally because I've forgotten that I've not run 'git whatever
> > --continue').
>
> Interesting. I think this would be a good default if we have an escape
> hatch (which could even come later). I often wander off to some other
> branch and go back. But then half the time I end up forgetting I'm in
> a middle of something and just "git rebase --quit" :P
>
> Of course with git-stash (*) and git-worktree, I guess there's little
> reason to just switch away from a something-in-progress worktree. I'll
> try to implement this in the next round, unless someone objects.

No objection here; I like this idea.

> (*) I hope git-stash remembers and restores "something-in-progress"
> status. Dunno. Never used it much.
>
> > > +-C <new-branch>::
> > > +--force-create <new-branch>::
> > > +     Similar to `--create` except that if `<new-branch>` already
> > > +     exists, it will be reset to `<start-point>`. This is a
> > > +     convenient shortcut for:
> >
> > If we're renaming the options to be more meaningful then maybe we should
> > consider a different name for this one - it has nothing to do with
> > creating a branch. Maybe --reset or --update?
>
> -C can also create a new branch like -c though. --reset or --update
> does not convey that (and --update sounds a bit too safe). Another
> option is --recreate.

Maybe --recreate, but I don't see as much of a problem with the
original name you gave the option.  Which name is better probably
depends on how you envision its usage.  If you view this option as
only being used if/when '-c' fails (perhaps Phillip sees it that
way?), then it'd make sense to use --recreate instead.  But if you
think some might adopt a workflow where they just use -C without first
trying -c ("create this branch, and I don't care if I made it before
just create it here"), then --force-create makes sense.

Another option, possibly showing my lack of understanding why this
flag was useful in the first place: just drop this set of flags from
this command.  People can switch to the branch and then use reset
--hard <startpoint>, right?  Or (if they don't care about the reflog,
which they probably don't) delete the branch first and then recreate
it?  Not sure why we need to give another way to do these operations.
(In contrast, I see -c as being used frequently enough to have merit
even if it could be implemented as two separate commands.)

I don't have much of an opinion about which of these three is the best
option here (I'm slightly biased towards just jettisoning the option,
but I understand there might be a good reason for it even if I don't
know what it is), so really I'm just giving some food for thought on
this one.

> > > +-f::
> > > +--force::
> > > +     Proceed even if the index or the working tree differs from
> > > +     HEAD. Both the index and working tree are restored to match
> > > +     the switching target. This is used to throw away local
> > > +     changes.
> >
> > I'd always thought that --force meant "throw away my local changes if
> > they conflict with the new branch" not "throw them away regardless"
> > (which is better as it is deterministic). Maybe we can come up with a
> > clearer name here --discard-changes? At the moment --force does not
> > throw away conflicts properly (see the script below in my comments about
> > --merge).
>
> Yeah if you want to redefine --force, now is a very good time.
> Personally I'd rather have separate options than the "one catch all"
> --force (or worse, multiple of --force). I'll leave this for the
> community to decide.
>
> Adding Elijah too. He also had some concern about "git restore
> --force". Maybe he's interested in this as well.

I like Phillip's suggestion of --discard-changes.  I also like how he
came up with a simple testcase showing one bug each with checkout's
current -m and -f handling; we should fix those.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (2 preceding siblings ...)
  2019-03-11 11:16       ` Phillip Wood
@ 2019-03-11 17:54       ` Elijah Newren
  2019-03-12 11:06         ` Phillip Wood
  2019-03-13 11:05         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
  3 siblings, 2 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-11 17:54 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

A few other comments that I thought of while responding elsewhere in
the thread that didn't make sense to include elsewhere...

On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> "git checkout" doing too many things is a source of confusion for many
> users (and it even bites old timers sometimes). To remedy that, the
> command will be split into two new ones: switch and
> something-to-checkout-paths. The good old "git checkout" command is

s/something-to-checkout-paths/restore/ ?

> +-g::
> +--guess::
> +       If `<branch>` is not found but there does exist a tracking
> +       branch in exactly one remote (call it `<remote>`) with a
> +       matching name, treat as equivalent to
> ++
> +------------
> +$ git switch -c <branch> --track <remote>/<branch>
> +------------

I'm not sure if it'd be better or worse to simplify this to
   git switch --track <remote>/<branch>
Thoughts?

> ++
> +If the branch exists in multiple remotes and one of them is named by
> +the `checkout.defaultRemote` configuration variable, we'll use that
> +one for the purposes of disambiguation, even if the `<branch>` isn't
> +unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
> +to always checkout remote branches from there if `<branch>` is
> +ambiguous but exists on the 'origin' remote. See also
> +`checkout.defaultRemote` in linkgit:git-config[1].
> +
> +-q::
> +--quiet::
> +       Quiet, suppress feedback messages.

--quiet and --progress should be adjacent in the man-page since they
are touching upon the same concept.  It'd be nice if we had a way to
group similar items like this besides simply putting them together,
but we can at least do that.

> +
> +-f::
> +--force::
> +       Proceed even if the index or the working tree differs from
> +       HEAD. Both the index and working tree are restored to match
> +       the switching target. This is used to throw away local
> +       changes.

--force (or --discard-changes as mentioned elsewhere in this thread),
-m/--merge, and --conflict are also similar options and should be
adjacent in the man-page; they all reflect on how to handle switching
when local changes affect the same paths that different between HEAD
and the other branch.

> +--progress::
> +--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`.

This again makes me curious what --quiet actually supresses; in the
case of branch switching, are there any non-warning informational
messages other than progress reports that are printed?  If not, the
extra sentence at the end of the description for --progress can (and
probably should) be stripped.

> +
> +-t::
> +--track::
> +       When creating a new branch, set up "upstream" configuration.
> +       `-c` is implied. See "--track" in linkgit:git-branch[1] for
> +       details.
> ++
> +If no `-c` option is given, the name of the new branch will be derived
> +from the remote-tracking branch, by looking at the local part of the
> +refspec configured for the corresponding remote, and then stripping
> +the initial part up to the "*".  This would tell us to use "hack" as
> +the local branch when branching off of "origin/hack" (or
> +"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
> +given name has no slash, or the above guessing results in an empty
> +name, the guessing is aborted.  You can explicitly give a name with
> +`-c` in such a case.

Slightly disappointed that we couldn't remove --track; it has always
seemed a bit complicated.  But I don't see how to do so, since people
do want upstream branches to be tracked with their new branches.

> +
> +--no-track::
> +       Do not set up "upstream" configuration, even if the
> +       branch.autoSetupMerge configuration variable is true.
> +
> +-m::
> +--merge::
> +       If you have local modifications to one or more files that are
> +       different between the current branch and the branch to which
> +       you are switching, the command refuses to switch branches in
> +       order to preserve your modifications in context.  However,
> +       with this option, a three-way merge between the current
> +       branch, your working tree contents, and the new branch is
> +       done, and you will be on the new branch.
> ++
> +When a merge conflict happens, the index entries for conflicting
> +paths are left unmerged, and you need to resolve the conflicts
> +and mark the resolved paths with `git add` (or `git rm` if the merge
> +should result in deletion of the path).

Now that Phillip highlighted issues with -m and -f, it's hard not to
wonder about other corner cases.  For example, what if the user made
some changes, staged them, then made more changes, then tried to 'git
checkout -m <other branch>'?  That's no longer a three-way merge, but
four way.  How does that work?  Does it just rely on merge-recursive's
(poorly defined) choice of when to bail out and when to permit such
craziness?

> +--conflict=<style>::
> +       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).
> +
> +--orphan <new-branch>::
> +       Create a new 'orphan' branch, named `<new-branch>`, started from
> +       `<start-point>` and switch to it. See explanation of the same
> +       option in linkgit:git-checkout[1] for details.

Sigh...does this mean --orphan will remain broken?  It has always
driven me crazy that it leaves you with a fully populated rather than
an empty index.  It seemed broken to me before I figured out the
special usecase, though it still seemed like the wrong default (an
empty index wouldn't surprise due to the "orphan" name, but a full one
does to those without the special usecase in mind).  Oh well, that's a
much smaller battle than the big picture of getting switch and restore
in place, and I don't want to derail the bigger picture; anything
using --orphan is a somewhat special case anyway.

> +You can give the `-m` flag to the command, which would try a three-way
> +merge:
> +
> +------------
> +$ git switch -m mytopic
> +Auto-merging frotz
> +------------
> +
> +After this three-way merge, the local modifications are _not_
> +registered in your index file, so `git diff` would show you what
> +changes you made since the tip of the new branch.

...even if the local modifications were registered in the index file
before?  Is this why Phillip's "x" was missing and how it avoided
doing a four-way merge?  I guess it kinda makes sense, view from this
angle, but I'm not so sure I like it.  Hmm....

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-10 22:57       ` Jacob Keller
@ 2019-03-11 19:00         ` Elijah Newren
  0 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-11 19:00 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Nguyễn Thái Ngọc Duy, Git Mailing List,
	Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Sun, Mar 10, 2019 at 5:29 PM Jacob Keller <jacob.keller@gmail.com> wrote:
>
> On March 8, 2019 1:57:41 AM PST, "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com> wrote:

> >+<new-branch>::
> >+      Name for the new branch.
> >+
> >+<start-point>::
> >+      The name of a commit at which to switch to before creating a
> >+      new branch or detach from.
>
> The wording here (and a few other places) feels awkward to me. I don't really have a better wording but maybe:
>
> ---
> The name of the commit to switch to when creating a new branch or detaching HEAD
> ---

I also struggle a bit to get better wording in this case.  But maybe
throwing another idea will inspire someone to synthesize (or rewrite)
these into something better, so...

--
The starting point for the new branch.  Specifying a <start-point>
allows you to create a branch based on some other point in history
than where HEAD currently points.  (Or, in the case of --detach,
allows you to inspect and detach from some other point.)
--

I know it's slightly on the longer side, but it's the best I could think of.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 17:24           ` Elijah Newren
@ 2019-03-11 20:51             ` Phillip Wood
  2019-03-11 22:04               ` Elijah Newren
  2019-03-12 12:19               ` Duy Nguyen
  2019-03-12 11:58             ` Duy Nguyen
  1 sibling, 2 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-11 20:51 UTC (permalink / raw)
  To: Elijah Newren, Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On 11/03/2019 17:24, Elijah Newren wrote:
> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
>>
>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>>
>>> Hi Duy
>>>
>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
>>>> "git checkout" doing too many things is a source of confusion for many
>>>> users (and it even bites old timers sometimes). To remedy that, the
>>>> command will be split into two new ones: switch and
>>>> something-to-checkout-paths.
>>>
>>> I think this is a good idea, thanks for working on it. I wonder if it
>>> would be a good idea to have the new command refuse to checkout a new
>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with
>>> an option to override the check) as switching branches in the middle of
>>> one of those is likely to be confusing to users (if I do it it is
>>> normally because I've forgotten that I've not run 'git whatever
>>> --continue').
>>
>> Interesting. I think this would be a good default if we have an escape
>> hatch (which could even come later). I often wander off to some other
>> branch and go back. But then half the time I end up forgetting I'm in
>> a middle of something and just "git rebase --quit" :P
>>
>> Of course with git-stash (*) and git-worktree, I guess there's little
>> reason to just switch away from a something-in-progress worktree. I'll
>> try to implement this in the next round, unless someone objects.
> 
> No objection here; I like this idea.
> 
>> (*) I hope git-stash remembers and restores "something-in-progress"
>> status. Dunno. Never used it much.
>>
>>>> +-C <new-branch>::
>>>> +--force-create <new-branch>::
>>>> +     Similar to `--create` except that if `<new-branch>` already
>>>> +     exists, it will be reset to `<start-point>`. This is a
>>>> +     convenient shortcut for:
>>>
>>> If we're renaming the options to be more meaningful then maybe we should
>>> consider a different name for this one - it has nothing to do with
>>> creating a branch. Maybe --reset or --update?
>>
>> -C can also create a new branch like -c though. --reset or --update
>> does not convey that (and --update sounds a bit too safe). Another
>> option is --recreate.
> 
> Maybe --recreate, but I don't see as much of a problem with the
> original name you gave the option.  Which name is better probably
> depends on how you envision its usage.  If you view this option as
> only being used if/when '-c' fails (perhaps Phillip sees it that
> way?), 

Yes I was thinking it was used to reset an existing branch in order to 
reuse the name, --recreate is a good suggestion in that context.

> then it'd make sense to use --recreate instead.  But if you
> think some might adopt a workflow where they just use -C without first
> trying -c ("create this branch, and I don't care if I made it before
> just create it here"), then --force-create makes sense.
> 
> Another option, possibly showing my lack of understanding why this
> flag was useful in the first place: just drop this set of flags from
> this command.  People can switch to the branch and then use reset
> --hard <startpoint>, right?  Or (if they don't care about the reflog,
> which they probably don't) delete the branch first and then recreate
> it?  Not sure why we need to give another way to do these operations.
> (In contrast, I see -c as being used frequently enough to have merit
> even if it could be implemented as two separate commands.)

I tend to agree with this but that's probably because I don't really use 
checkout -B. I'm not sure if it's widely used or not. I do find checkout 
-b convenient though.

> I don't have much of an opinion about which of these three is the best
> option here (I'm slightly biased towards just jettisoning the option,
> but I understand there might be a good reason for it even if I don't
> know what it is), so really I'm just giving some food for thought on
> this one.
> 
>>>> +-f::
>>>> +--force::
>>>> +     Proceed even if the index or the working tree differs from
>>>> +     HEAD. Both the index and working tree are restored to match
>>>> +     the switching target. This is used to throw away local
>>>> +     changes.
>>>
>>> I'd always thought that --force meant "throw away my local changes if
>>> they conflict with the new branch" not "throw them away regardless"
>>> (which is better as it is deterministic). Maybe we can come up with a
>>> clearer name here --discard-changes? At the moment --force does not
>>> throw away conflicts properly (see the script below in my comments about
>>> --merge).
>>
>> Yeah if you want to redefine --force, now is a very good time.
>> Personally I'd rather have separate options than the "one catch all"
>> --force (or worse, multiple of --force). I'll leave this for the
>> community to decide.
>>
>> Adding Elijah too. He also had some concern about "git restore
>> --force". Maybe he's interested in this as well.
> 
> I like Phillip's suggestion of --discard-changes.  I also like how he
> came up with a simple testcase showing one bug each with checkout's
> current -m and -f handling; we should fix those.

With regard to discarding conflicts, do we want it to clear up any state 
associated with the conflicts (like reset)? They rarely happen in 
isolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure 
what it should do in the middle of a rebase or when cherry-picking a 
range of commits. I think it would be surprising if it was the 
equivalent of rebase/cherry-pick --quit but just clearing the conflicts 
in those contexts may not be very useful in practice.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 20:51             ` Phillip Wood
@ 2019-03-11 22:04               ` Elijah Newren
  2019-03-12 10:58                 ` Phillip Wood
  2019-03-12 12:19               ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-11 22:04 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Mon, Mar 11, 2019 at 1:51 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 11/03/2019 17:24, Elijah Newren wrote:
> > On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
> >> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> >>>> "git checkout" doing too many things is a source of confusion for many
> >>>> users (and it even bites old timers sometimes). To remedy that, the
> >>>> command will be split into two new ones: switch and
> >>>> something-to-checkout-paths.
> >>>
> >>> I think this is a good idea, thanks for working on it. I wonder if it
> >>> would be a good idea to have the new command refuse to checkout a new
> >>> branch if there is a cherry-pick/revert/merge/rebase in progress (with
> >>> an option to override the check) as switching branches in the middle of
> >>> one of those is likely to be confusing to users (if I do it it is
> >>> normally because I've forgotten that I've not run 'git whatever
> >>> --continue').
> >>
> >> Interesting. I think this would be a good default if we have an escape
> >> hatch (which could even come later). I often wander off to some other
> >> branch and go back. But then half the time I end up forgetting I'm in
> >> a middle of something and just "git rebase --quit" :P
> >>
> >> Of course with git-stash (*) and git-worktree, I guess there's little
> >> reason to just switch away from a something-in-progress worktree. I'll
> >> try to implement this in the next round, unless someone objects.
> >
> > No objection here; I like this idea.
> >

Keeping this hunk since it's now relevant to the comment below...

> >>>> +-f::
> >>>> +--force::
> >>>> +     Proceed even if the index or the working tree differs from
> >>>> +     HEAD. Both the index and working tree are restored to match
> >>>> +     the switching target. This is used to throw away local
> >>>> +     changes.
> >>>
> >>> I'd always thought that --force meant "throw away my local changes if
> >>> they conflict with the new branch" not "throw them away regardless"
> >>> (which is better as it is deterministic). Maybe we can come up with a
> >>> clearer name here --discard-changes? At the moment --force does not
> >>> throw away conflicts properly (see the script below in my comments about
> >>> --merge).
> >>
> >> Yeah if you want to redefine --force, now is a very good time.
> >> Personally I'd rather have separate options than the "one catch all"
> >> --force (or worse, multiple of --force). I'll leave this for the
> >> community to decide.
> >>
> >> Adding Elijah too. He also had some concern about "git restore
> >> --force". Maybe he's interested in this as well.
> >
> > I like Phillip's suggestion of --discard-changes.  I also like how he
> > came up with a simple testcase showing one bug each with checkout's
> > current -m and -f handling; we should fix those.
>
> With regard to discarding conflicts, do we want it to clear up any state
> associated with the conflicts (like reset)? They rarely happen in
> isolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure
> what it should do in the middle of a rebase or when cherry-picking a
> range of commits. I think it would be surprising if it was the
> equivalent of rebase/cherry-pick --quit but just clearing the conflicts
> in those contexts may not be very useful in practice.

You already suggested above (outside the context of --discard-changes)
that we should just error out if there is some special mid-operation
state (be it from a merge, cherry-pick, rebase, or bisect).  The user
can then manually resolve the operation first, or, perhaps use a
special override to force the switch command to proceed despite the
presence of mid-operation state.

Personally, I'm leaning towards --discard-changes operating within
that same context; I think that mid-operation special state should
require a more explicit and operation-specific step to remove (e.g.
rebase --quit).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 22:04               ` Elijah Newren
@ 2019-03-12 10:58                 ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-12 10:58 UTC (permalink / raw)
  To: Elijah Newren, Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Hi Elijah

On 11/03/2019 22:04, Elijah Newren wrote:
> On Mon, Mar 11, 2019 at 1:51 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 11/03/2019 17:24, Elijah Newren wrote:
>>> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
>>>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
>>>>>> "git checkout" doing too many things is a source of confusion for many
>>>>>> users (and it even bites old timers sometimes). To remedy that, the
>>>>>> command will be split into two new ones: switch and
>>>>>> something-to-checkout-paths.
>>>>>
>>>>> I think this is a good idea, thanks for working on it. I wonder if it
>>>>> would be a good idea to have the new command refuse to checkout a new
>>>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with
>>>>> an option to override the check) as switching branches in the middle of
>>>>> one of those is likely to be confusing to users (if I do it it is
>>>>> normally because I've forgotten that I've not run 'git whatever
>>>>> --continue').
>>>>
>>>> Interesting. I think this would be a good default if we have an escape
>>>> hatch (which could even come later). I often wander off to some other
>>>> branch and go back. But then half the time I end up forgetting I'm in
>>>> a middle of something and just "git rebase --quit" :P
>>>>
>>>> Of course with git-stash (*) and git-worktree, I guess there's little
>>>> reason to just switch away from a something-in-progress worktree. I'll
>>>> try to implement this in the next round, unless someone objects.
>>>
>>> No objection here; I like this idea.
>>>
> 
> Keeping this hunk since it's now relevant to the comment below...
> 
>>>>>> +-f::
>>>>>> +--force::
>>>>>> +     Proceed even if the index or the working tree differs from
>>>>>> +     HEAD. Both the index and working tree are restored to match
>>>>>> +     the switching target. This is used to throw away local
>>>>>> +     changes.
>>>>>
>>>>> I'd always thought that --force meant "throw away my local changes if
>>>>> they conflict with the new branch" not "throw them away regardless"
>>>>> (which is better as it is deterministic). Maybe we can come up with a
>>>>> clearer name here --discard-changes? At the moment --force does not
>>>>> throw away conflicts properly (see the script below in my comments about
>>>>> --merge).
>>>>
>>>> Yeah if you want to redefine --force, now is a very good time.
>>>> Personally I'd rather have separate options than the "one catch all"
>>>> --force (or worse, multiple of --force). I'll leave this for the
>>>> community to decide.
>>>>
>>>> Adding Elijah too. He also had some concern about "git restore
>>>> --force". Maybe he's interested in this as well.
>>>
>>> I like Phillip's suggestion of --discard-changes.  I also like how he
>>> came up with a simple testcase showing one bug each with checkout's
>>> current -m and -f handling; we should fix those.
>>
>> With regard to discarding conflicts, do we want it to clear up any state
>> associated with the conflicts (like reset)? They rarely happen in
>> isolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure
>> what it should do in the middle of a rebase or when cherry-picking a
>> range of commits. I think it would be surprising if it was the
>> equivalent of rebase/cherry-pick --quit but just clearing the conflicts
>> in those contexts may not be very useful in practice.
> 
> You already suggested above (outside the context of --discard-changes)
> that we should just error out if there is some special mid-operation
> state (be it from a merge, cherry-pick, rebase, or bisect).  The user
> can then manually resolve the operation first, or, perhaps use a
> special override to force the switch command to proceed despite the
> presence of mid-operation state.
> 
> Personally, I'm leaning towards --discard-changes operating within
> that same context; I think that mid-operation special state should
> require a more explicit and operation-specific step to remove (e.g.
> rebase --quit).

I think that makes sense, I was wondering if --discard-changes should 
remove the state of a conflicted merge/single cherry-pick/revert but it 
is probably simpler and easier to understand just to error out. I think 
it should still clean up conflicts from other checkouts and applying 
stashes though as there is no other state in those cases.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 17:54       ` Elijah Newren
@ 2019-03-12 11:06         ` Phillip Wood
  2019-03-12 16:43           ` Elijah Newren
  2019-03-13 11:05         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-12 11:06 UTC (permalink / raw)
  To: Elijah Newren, Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

Hi Elijah

On 11/03/2019 17:54, Elijah Newren wrote:
> A few other comments that I thought of while responding elsewhere in
> the thread that didn't make sense to include elsewhere...
> 
> On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>
>> +-m::
>> +--merge::
>> +       If you have local modifications to one or more files that are
>> +       different between the current branch and the branch to which
>> +       you are switching, the command refuses to switch branches in
>> +       order to preserve your modifications in context.  However,
>> +       with this option, a three-way merge between the current
>> +       branch, your working tree contents, and the new branch is
>> +       done, and you will be on the new branch.
>> ++
>> +When a merge conflict happens, the index entries for conflicting
>> +paths are left unmerged, and you need to resolve the conflicts
>> +and mark the resolved paths with `git add` (or `git rm` if the merge
>> +should result in deletion of the path).
> 
> Now that Phillip highlighted issues with -m and -f, it's hard not to
> wonder about other corner cases.  For example, what if the user made
> some changes, staged them, then made more changes, then tried to 'git
> checkout -m <other branch>'?  That's no longer a three-way merge, but
> four way.  How does that work?  Does it just rely on merge-recursive's
> (poorly defined) choice of when to bail out and when to permit such
> craziness?

If the two-way merge fails then it does 'git add -u' before calling 
merge_recursive(), then any merged paths are reset to the new HEAD 
(which throws away newly added files, it should keep anything that is 
not in HEAD or HEAD@{1}). So any staged changes are lost.

>> +--orphan <new-branch>::
>> +       Create a new 'orphan' branch, named `<new-branch>`, started from
>> +       `<start-point>` and switch to it. See explanation of the same
>> +       option in linkgit:git-checkout[1] for details.
> 
> Sigh...does this mean --orphan will remain broken?  It has always
> driven me crazy that it leaves you with a fully populated rather than
> an empty index.

I've always thought that was weird.

> It seemed broken to me before I figured out the
> special usecase,

I haven't figured it out yet - what is it?


Best Wishes

Phillip

> though it still seemed like the wrong default (an
> empty index wouldn't surprise due to the "orphan" name, but a full one
> does to those without the special usecase in mind).  Oh well, that's a
> much smaller battle than the big picture of getting switch and restore
> in place, and I don't want to derail the bigger picture; anything
> using --orphan is a somewhat special case anyway.
> 
>> +You can give the `-m` flag to the command, which would try a three-way
>> +merge:
>> +
>> +------------
>> +$ git switch -m mytopic
>> +Auto-merging frotz
>> +------------
>> +
>> +After this three-way merge, the local modifications are _not_
>> +registered in your index file, so `git diff` would show you what
>> +changes you made since the tip of the new branch.
> 
> ...even if the local modifications were registered in the index file
> before?  Is this why Phillip's "x" was missing and how it avoided
> doing a four-way merge?  I guess it kinda makes sense, view from this
> angle, but I'm not so sure I like it.  Hmm....
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 17:03           ` Phillip Wood
@ 2019-03-12 11:54             ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-12 11:54 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren, Elijah Newren

On Tue, Mar 12, 2019 at 12:03 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Duy
>
> On 11/03/2019 11:47, Duy Nguyen wrote:
> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >>
> >>
> >> Hi Duy
> >>
> >> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> >>> "git checkout" doing too many things is a source of confusion for many
> >>> users (and it even bites old timers sometimes). To remedy that, the
> >>> command will be split into two new ones: switch and
> >>> something-to-checkout-paths.
> >>
> >> I think this is a good idea, thanks for working on it. I wonder if it
> >> would be a good idea to have the new command refuse to checkout a new
> >> branch if there is a cherry-pick/revert/merge/rebase in progress (with
> >> an option to override the check) as switching branches in the middle of
> >> one of those is likely to be confusing to users (if I do it it is
> >> normally because I've forgotten that I've not run 'git whatever
> >> --continue').
> >
> > Interesting. I think this would be a good default if we have an escape
> > hatch (which could even come later). I often wander off to some other
> > branch and go back. But then half the time I end up forgetting I'm in
> > a middle of something and just "git rebase --quit" :P
> >
> > Of course with git-stash (*) and git-worktree, I guess there's little
> > reason to just switch away from a something-in-progress worktree. I'll
> > try to implement this in the next round, unless someone objects.
> >
> > (*) I hope git-stash remembers and restores "something-in-progress"
> > status. Dunno. Never used it much.
>
> I don't think it does. For rebase it's non trivial as it needs to
> remember the refs under refs/rewritten and stop gc from collecting any
> of them or the original head (in theory the todo list can contain
> commits that the user has added from other branches as well so they'd
> also need to be protected from gc). For cherry-pick there are gc issues
> as well.

gc issues should be fixed anyway because gc could start any time (even
manually by the user). And teaching pack-objects and friends to not
delete original head and the todo list does not look so hard.
#leftovers stuff?
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 17:24           ` Elijah Newren
  2019-03-11 20:51             ` Phillip Wood
@ 2019-03-12 11:58             ` Duy Nguyen
  2019-03-12 17:05               ` Elijah Newren
  1 sibling, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-12 11:58 UTC (permalink / raw)
  To: Elijah Newren, Phillip Wood
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
> >
> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > >
> > > Hi Duy
> > >
> > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > > > "git checkout" doing too many things is a source of confusion for many
> > > > users (and it even bites old timers sometimes). To remedy that, the
> > > > command will be split into two new ones: switch and
> > > > something-to-checkout-paths.
> > >
> > > I think this is a good idea, thanks for working on it. I wonder if it
> > > would be a good idea to have the new command refuse to checkout a new
> > > branch if there is a cherry-pick/revert/merge/rebase in progress (with
> > > an option to override the check) as switching branches in the middle of
> > > one of those is likely to be confusing to users (if I do it it is
> > > normally because I've forgotten that I've not run 'git whatever
> > > --continue').
> >
> > Interesting. I think this would be a good default if we have an escape
> > hatch (which could even come later). I often wander off to some other
> > branch and go back. But then half the time I end up forgetting I'm in
> > a middle of something and just "git rebase --quit" :P
> >
> > Of course with git-stash (*) and git-worktree, I guess there's little
> > reason to just switch away from a something-in-progress worktree. I'll
> > try to implement this in the next round, unless someone objects.
>
> No objection here; I like this idea.

One last thing. What about --detach? Should it have the same
protection or should we let the user doing --detach (experiments) take
the responsibity to not screw themselves up?
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 20:51             ` Phillip Wood
  2019-03-11 22:04               ` Elijah Newren
@ 2019-03-12 12:19               ` Duy Nguyen
  2019-03-12 15:36                 ` Eric Sunshine
  1 sibling, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-12 12:19 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Elijah Newren, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > then it'd make sense to use --recreate instead.  But if you
> > think some might adopt a workflow where they just use -C without first
> > trying -c ("create this branch, and I don't care if I made it before
> > just create it here"), then --force-create makes sense.
> >
> > Another option, possibly showing my lack of understanding why this
> > flag was useful in the first place: just drop this set of flags from
> > this command.  People can switch to the branch and then use reset
> > --hard <startpoint>, right?  Or (if they don't care about the reflog,
> > which they probably don't) delete the branch first and then recreate
> > it?  Not sure why we need to give another way to do these operations.
> > (In contrast, I see -c as being used frequently enough to have merit
> > even if it could be implemented as two separate commands.)
>
> I tend to agree with this but that's probably because I don't really use
> checkout -B. I'm not sure if it's widely used or not. I do find checkout
> -b convenient though.

Yeah I think both -b and -B are about convenience.

Another point is multiple steps of switching and resetting could be
really painful for people with large worktrees. Doing everything in
one go is probably preferable because it generates less I/O.

The mention of "git reset --hard" triggers me because I secretly want
to replace that command ("git reset", all modes), and in the last
"promotion" patch, "reset --hard" is replaced with "switch -C" three
times. We get really close to replacing git-reset if we have something
to say "-C <current branch>". Whether this operation fits in the
"switching things" mentality, I'm still not sure.

But I would not mind dropping -C for now, if people think it's not
that useful. We can bring it back in incremental updates if we realize
we miss it so much. I'll keep it unless somebody says something.

PS. The same probably goes for --orphan too. Wait and see if people
complain, then we know how they actually use it.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 12:19               ` Duy Nguyen
@ 2019-03-12 15:36                 ` Eric Sunshine
  2019-03-12 16:51                   ` Elijah Newren
  0 siblings, 1 reply; 289+ messages in thread
From: Eric Sunshine @ 2019-03-12 15:36 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Elijah Newren, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > I tend to agree with this but that's probably because I don't really use
> > checkout -B. I'm not sure if it's widely used or not. I do find checkout
> > -b convenient though.
>
> Yeah I think both -b and -B are about convenience.
>
> But I would not mind dropping -C for now, if people think it's not
> that useful. We can bring it back in incremental updates if we realize
> we miss it so much. I'll keep it unless somebody says something.

It's not much of a datapoint, but I do use "git checkout -B" (and
therefore would use "git switch -C") periodically (in addition to
-b/-c, which I use all the time). And, convenience is important,
especially considering that "git switch" is already more painful in
some ways than "git checkout", due to having to trigger and spell out
certain things explicitly (such as detaching).

> PS. The same probably goes for --orphan too. Wait and see if people
> complain, then we know how they actually use it.

Again, not much of a datapoint, but I do use --orphan periodically.
The idea of "fixing" the behavior so that --orphan starts with a clean
slate is certainly appealing (since it matches how I've used orphan
branches in each case).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 11:06         ` Phillip Wood
@ 2019-03-12 16:43           ` Elijah Newren
  2019-03-14 11:00             ` Phillip Wood
  0 siblings, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-12 16:43 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Nguyễn Thái Ngọc Duy, Git Mailing List,
	Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Tue, Mar 12, 2019 at 4:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Elijah
>
> On 11/03/2019 17:54, Elijah Newren wrote:
> > A few other comments that I thought of while responding elsewhere in
> > the thread that didn't make sense to include elsewhere...
> >
> > On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> >>
> >> +-m::
> >> +--merge::
> >> +       If you have local modifications to one or more files that are
> >> +       different between the current branch and the branch to which
> >> +       you are switching, the command refuses to switch branches in
> >> +       order to preserve your modifications in context.  However,
> >> +       with this option, a three-way merge between the current
> >> +       branch, your working tree contents, and the new branch is
> >> +       done, and you will be on the new branch.
> >> ++
> >> +When a merge conflict happens, the index entries for conflicting
> >> +paths are left unmerged, and you need to resolve the conflicts
> >> +and mark the resolved paths with `git add` (or `git rm` if the merge
> >> +should result in deletion of the path).
> >
> > Now that Phillip highlighted issues with -m and -f, it's hard not to
> > wonder about other corner cases.  For example, what if the user made
> > some changes, staged them, then made more changes, then tried to 'git
> > checkout -m <other branch>'?  That's no longer a three-way merge, but
> > four way.  How does that work?  Does it just rely on merge-recursive's
> > (poorly defined) choice of when to bail out and when to permit such
> > craziness?
>
> If the two-way merge fails then it does 'git add -u' before calling
> merge_recursive(), then any merged paths are reset to the new HEAD
> (which throws away newly added files, it should keep anything that is
> not in HEAD or HEAD@{1}). So any staged changes are lost.

Ah, so roughly
  * git add -u
  * uncommitted_tree=$(git write-tree)
  * git reset --hard
  * git checkout $other_branch
  * git merge-recursive $old_branch -- $other_branch $uncommitted_tree
  * git reset --mixed HEAD

This at least gives well defined behavior, even if somewhat suboptimal
in relation to losing staged changes (especially when those staged
changes were new files).

I wonder if it'd be nicer, after I get my don't-touch-the-working-tree
merge rewrite done, to instead do something like:
  * Write the beginning index to a tree; call it $tree_0
  * Note whether any working tree files differ from the index, add
these all to a temporary index and write to to a tree; call it
$tree_1.
  * Do a three way in-memory merge of $old_branch with $other_branch
and $tree_0; call it $merged_tree if there are no conflicts
  * If $tree_0 == $tree_1, checkout the new branch and update the
index and working tree to reflect the merge result.
  * If $tree_0 != $tree_1 and there were any conflicts, abort telling
the user they need to either unstage or stage changes first (we don't
want to confuse users with a merge of a merge).
  * Switch to the new branch, and update the index to match $merged_tree
  * Do a three way in-memory merge of $old_branch with $merged_tree
and $tree_1, writing the results (including any conflicts) to the
working tree afterward.

Pros of this method:
  * We don't lose newly staged files
  * We don't lose user's carefully staged entries for existing files either
Cons of this method:
  * It may abort with an error if the user has a mix of both staged
and unstaged changes (in particular, it will do so if the user's
staged changes conflict with some difference in the new branch)


Thoughts?

>
> >> +--orphan <new-branch>::
> >> +       Create a new 'orphan' branch, named `<new-branch>`, started from
> >> +       `<start-point>` and switch to it. See explanation of the same
> >> +       option in linkgit:git-checkout[1] for details.
> >
> > Sigh...does this mean --orphan will remain broken?  It has always
> > driven me crazy that it leaves you with a fully populated rather than
> > an empty index.
>
> I've always thought that was weird.
>
> > It seemed broken to me before I figured out the
> > special usecase,
>
> I haven't figured it out yet - what is it?

It's a presumption that despite the fact that you want a new branch,
and one with no history to boot, that for some reason you want all the
previous branch's current contents.  In particular, you can think of
it as a way to squash all the history of an existing branch into a
single commit in a new branch.

Knowing of this usecase doesn't make it bother me any less when I want
to create a new unrelated empty branch; it seems like it took the
esoteric usecase over the common one to me, but I'm biased.  It makes
me feel better than neither you nor Eric could understand this
behavior of --orphan either.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 15:36                 ` Eric Sunshine
@ 2019-03-12 16:51                   ` Elijah Newren
  2019-03-12 17:28                     ` Eric Sunshine
  2019-03-15  6:11                     ` Jacob Keller
  0 siblings, 2 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-12 16:51 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > > I tend to agree with this but that's probably because I don't really use
> > > checkout -B. I'm not sure if it's widely used or not. I do find checkout
> > > -b convenient though.
> >
> > Yeah I think both -b and -B are about convenience.
> >
> > But I would not mind dropping -C for now, if people think it's not
> > that useful. We can bring it back in incremental updates if we realize
> > we miss it so much. I'll keep it unless somebody says something.
>
> It's not much of a datapoint, but I do use "git checkout -B" (and
> therefore would use "git switch -C") periodically (in addition to
> -b/-c, which I use all the time). And, convenience is important,
> especially considering that "git switch" is already more painful in
> some ways than "git checkout", due to having to trigger and spell out
> certain things explicitly (such as detaching).

Ooh, interesting.  I haven't used it and didn't know who did, but
since you do you can probably answer the question surrounding the
long-name for the -C option from earlier in the thread:

Do you use checkout -B only when checkout -b fails, or do you use it
pre-emptively?  The former would suggest we should use a name like
--recreate, while the latter would suggest a name more like
--force-create.

> > PS. The same probably goes for --orphan too. Wait and see if people
> > complain, then we know how they actually use it.
>
> Again, not much of a datapoint, but I do use --orphan periodically.
> The idea of "fixing" the behavior so that --orphan starts with a clean
> slate is certainly appealing (since it matches how I've used orphan
> branches in each case).

The only three people who have commented on --orphan in this thread
all apparently feel the same way: the current behavior is wrong.
Maybe we can switch it to start with an empty index after all?

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 11:58             ` Duy Nguyen
@ 2019-03-12 17:05               ` Elijah Newren
  2019-03-14 10:42                 ` Phillip Wood
  0 siblings, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-12 17:05 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 4:58 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:
> > On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:

> > > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > > > > "git checkout" doing too many things is a source of confusion for many
> > > > > users (and it even bites old timers sometimes). To remedy that, the
> > > > > command will be split into two new ones: switch and
> > > > > something-to-checkout-paths.
> > > >
> > > > I think this is a good idea, thanks for working on it. I wonder if it
> > > > would be a good idea to have the new command refuse to checkout a new
> > > > branch if there is a cherry-pick/revert/merge/rebase in progress (with
> > > > an option to override the check) as switching branches in the middle of
> > > > one of those is likely to be confusing to users (if I do it it is
> > > > normally because I've forgotten that I've not run 'git whatever
> > > > --continue').
> > >
> > > Interesting. I think this would be a good default if we have an escape
> > > hatch (which could even come later). I often wander off to some other
> > > branch and go back. But then half the time I end up forgetting I'm in
> > > a middle of something and just "git rebase --quit" :P
> > >
> > > Of course with git-stash (*) and git-worktree, I guess there's little
> > > reason to just switch away from a something-in-progress worktree. I'll
> > > try to implement this in the next round, unless someone objects.
> >
> > No objection here; I like this idea.
>
> One last thing. What about --detach? Should it have the same
> protection or should we let the user doing --detach (experiments) take
> the responsibity to not screw themselves up?

My intuition here is a bit weaker; it would be nice to hear others'
opinions.  My best guess thinking it over a bit is that while someone
doing --detach is more likely to know what they are doing than other
users of the 'switch' command, they may also be the kind of person who
is more likely to run interactive rebases and thus had more
opportunities to forget that they are still in the middle of one.  I
think I've been guilty of that a few times.  So, while this would be
less critical for this case, I lean towards saying that it may still
be helpful anyway, and if nothing else the consistency of handling all
switching cases the same seems beneficial.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 16:51                   ` Elijah Newren
@ 2019-03-12 17:28                     ` Eric Sunshine
  2019-03-13  1:55                       ` Junio C Hamano
  2019-03-14  9:17                       ` Duy Nguyen
  2019-03-15  6:11                     ` Jacob Keller
  1 sibling, 2 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-12 17:28 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 12:51 PM Elijah Newren <newren@gmail.com> wrote:
> On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > It's not much of a datapoint, but I do use "git checkout -B" (and
> > therefore would use "git switch -C") periodically (in addition to
> > -b/-c, which I use all the time). And, convenience is important,
> > especially considering that "git switch" is already more painful in
> > some ways than "git checkout", due to having to trigger and spell out
> > certain things explicitly (such as detaching).
>
> Ooh, interesting.  I haven't used it and didn't know who did, but
> since you do you can probably answer the question surrounding the
> long-name for the -C option from earlier in the thread:
>
> Do you use checkout -B only when checkout -b fails, or do you use it
> pre-emptively?  The former would suggest we should use a name like
> --recreate, while the latter would suggest a name more like
> --force-create.

It doesn't come up often, but I use "git checkout -B" when I know that
I want to start an existing branch over from scratch to build upon
some unrelated branch. This almost always happens when I'm programming
experimentally. I'll create a new branch with the desired _final_
name, start work on it, realize that some other approach might be
better, so set that aside and create a new branch with some junk name,
work on that for a while, possibly repeat, creating more branches with
more junk names, etc. Sometimes the result is that bits a pieces of
the various junk-named branches can be pulled together into a final
product, in which case I may "git checkout -B <final>", overwriting
the first branch I created, and then pull a few things from other
junk-named branches to reach the end. (More often, though, one of the
junk-named branches ends up being what I want to go with, so I just
"git branch -M <final>", throwing away the original branch, and
keeping the junk-named one but with the _final_ name). Sorry for the
too long-winded explanation.

I think --force-create is a better name because "force" implies
strongly that you're doing something potentially dangerous. Also,
every option in git-branch which deletes or overwrites a branch name
requires "force", so it's good precedent to follow.

> > Again, not much of a datapoint, but I do use --orphan periodically.
> > The idea of "fixing" the behavior so that --orphan starts with a clean
> > slate is certainly appealing (since it matches how I've used orphan
> > branches in each case).
>
> The only three people who have commented on --orphan in this thread
> all apparently feel the same way: the current behavior is wrong.
> Maybe we can switch it to start with an empty index after all?

Starting empty may match intuition better. (More importantly, perhaps,
it's harder to come up with a use-case for --orphan which doesn't
involve starting with a clean slate.)

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 17:28                     ` Eric Sunshine
@ 2019-03-13  1:55                       ` Junio C Hamano
  2019-03-14  9:17                       ` Duy Nguyen
  1 sibling, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-13  1:55 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Elijah Newren, Duy Nguyen, Phillip Wood, Git Mailing List,
	SZEDER Gábor, Martin Ågren

Eric Sunshine <sunshine@sunshineco.com> writes:

>> Do you use checkout -B only when checkout -b fails, or do you use it
>> pre-emptively?  The former would suggest we should use a name like
>> --recreate, while the latter would suggest a name more like
>> --force-create.
>
> It doesn't come up often, but I use "git checkout -B" when I know that
> I want to start an existing branch over from scratch to build upon
> some unrelated branch.

FWIW, I always use "checkout -B" at least twice during an
integration cycle.  "git checkout -B jch master" before rebuilding
my private edition with select topics (including all the topics
already in 'next'), plus "git checkout -B pu jch" before rebuilding
the 'pu' branch with the other topics I happen to have looked at.
These are similar to your use case---the branches have constant
"purpose" and always exist, but they are rebuilt from scratch when
they get updated.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 17:54       ` Elijah Newren
  2019-03-12 11:06         ` Phillip Wood
@ 2019-03-13 11:05         ` Duy Nguyen
  2019-03-13 14:36           ` Elijah Newren
  1 sibling, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-13 11:05 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Tue, Mar 12, 2019 at 12:54 AM Elijah Newren <newren@gmail.com> wrote:
> > +--progress::
> > +--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`.
>
> This again makes me curious what --quiet actually supresses; in the
> case of branch switching, are there any non-warning informational
> messages other than progress reports that are printed?

One big thing git-checkout and git-switch will print when not --quiet
is "git diff --name-status" to highlight local changes since we allow
switching branches when the worktree is not clean. Should it be
mentioned in --quiet description?

We could also occasionally print advice, branch tracking info, and
one-liner summary like "Switched to (new) branch 'master'".
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-13 11:05         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
@ 2019-03-13 14:36           ` Elijah Newren
  0 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-13 14:36 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Wed, Mar 13, 2019 at 4:05 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Tue, Mar 12, 2019 at 12:54 AM Elijah Newren <newren@gmail.com> wrote:
> > > +--progress::
> > > +--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`.
> >
> > This again makes me curious what --quiet actually supresses; in the
> > case of branch switching, are there any non-warning informational
> > messages other than progress reports that are printed?
>
> One big thing git-checkout and git-switch will print when not --quiet
> is "git diff --name-status" to highlight local changes since we allow
> switching branches when the worktree is not clean. Should it be
> mentioned in --quiet description?

Nah, that all makes sense.  It was more a curiosity (I tend to have a
clean working tree most the time when switching branches, I guess;
maybe I also filtered out messages I was used to seeing and couldn't
remember seeing them) and also I had a small concern that perhaps this
bit of documentation was relevant for one part of git-checkout and not
the other, and thus shouldn't appear in both git-switch and
git-restore.

> We could also occasionally print advice, branch tracking info, and
> one-liner summary like "Switched to (new) branch 'master'".

Thanks for the explanations.  This all makes sense.  However, all
these answers are specific to git-switch; perhaps the detailed
explanation of --progress can be shorted for git-restore?  Or does it
too have reasons to print non-progress informational messages?

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-08  9:57     ` [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
@ 2019-03-13 18:36       ` Eckhard Maaß
  2019-03-15  8:19         ` Eric Sunshine
  2019-03-16  3:59         ` Duy Nguyen
  0 siblings, 2 replies; 289+ messages in thread
From: Eckhard Maaß @ 2019-03-13 18:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, gitster, sunshine, szeder.dev

On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:
> Similar to automatic detach, this behavior could be confusing because
> it can sometimes create a new branch without a user asking it to,
> especially when the user is still not aware about this feature.
> 
> In the future, perhaps we could have a config key to disable these
> safety nets and let 'switch' do automatic detach or dwim
> again. But that will be opt-in after the user knows what is what. For
> now give a short option if you want to use it often.

As I am late to the patch series (sorry!), has there been already any
discussion on that? In my experience, people get confused with detached
HEAD state quite often, whereas the automatic creation of a local branch
is no problem. So if it is deemed to be too confusing to dwim in this
case, could we add a hint suggesting the command? Something like:
"No suitable branch <foo> found, however there is a remote tracking
branch <origin/foo> that you can siwtch and create with `git switch
--guess foo`" (or maybe the one without guess)?

And while at it - what should happen, if:

- there is a tag named example
- no local branch example
- a branch at origin/example

... and we switch then? Right now it just gives "cannot find branch",
should there be more information? Should it even create a branch
example? With switch, switching a branch is unambiguous, even though
there is a tag with that name. If I really want to --guess - should I be
given more information?

Greetings,
Eckhard

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:16       ` Phillip Wood
  2019-03-11 11:47         ` Duy Nguyen
@ 2019-03-14  3:29         ` Duy Nguyen
  2019-03-14  5:59           ` Elijah Newren
  2019-03-14  4:39         ` Junio C Hamano
                           ` (3 subsequent siblings)
  5 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-14  3:29 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren, Elijah Newren

On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > +-m::
> > +--merge::
> > +     If you have local modifications to one or more files that are
> > +     different between the current branch and the branch to which
> > +     you are switching, the command refuses to switch branches in
> > +     order to preserve your modifications in context.  However,
> > +     with this option, a three-way merge between the current
> > +     branch, your working tree contents, and the new branch is
> > +     done, and you will be on the new branch.
>
> I was wondering what people felt about making this the default for the
> new command. If I'm carrying changes over to the new branch then I want
> them to be merged in, it's annoying to have them carried over most of
> the time but then sometimes have the checkout fail without specifying
> --merge.

If my worktree has local changes and I accidentally switch to a
different branch, I could switch back without losing any local changes
and the tree I have is exactly what I had before the switch. Is this
still true if -m is made default?

I think sometimes a 3-way merge creates marker conflicts in file, and
this does not look easy to reverse when switching back. If it's true
and we can detect it, we can still abort this case (i.e. requiring -m
to continue) while allowing succesful 3-way merges by default. But are
successful 3-way merges reversible?
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:16       ` Phillip Wood
  2019-03-11 11:47         ` Duy Nguyen
  2019-03-14  3:29         ` Duy Nguyen
@ 2019-03-14  4:39         ` Junio C Hamano
  2019-03-14 14:13         ` Duy Nguyen
                           ` (2 subsequent siblings)
  5 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-14  4:39 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Nguyễn Thái Ngọc Duy, git, sunshine, szeder.dev,
	Martin Ågren

Phillip Wood <phillip.wood123@gmail.com> writes:

>> +-m::
>> +--merge::
>> +	If you have local modifications to one or more files that are
>> +	different between the current branch and the branch to which
>> +	you are switching, the command refuses to switch branches in
>> +	order to preserve your modifications in context.  However,
>> +	with this option, a three-way merge between the current
>> +	branch, your working tree contents, and the new branch is
>> +	done, and you will be on the new branch.
>
> I was wondering what people felt about making this the default for the
> new command.

Even its inventor (me) never runs "-m" without first seeing checkout
without "-m" fail, and seeing how much can potentially be lost if
the branch switch conflicts by asking "git diff" (no other
options).  It is very unadvisable to make it the default.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14  3:29         ` Duy Nguyen
@ 2019-03-14  5:59           ` Elijah Newren
  2019-03-14  7:23             ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-14  5:59 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Wed, Mar 13, 2019 at 8:29 PM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > > +-m::
> > > +--merge::
> > > +     If you have local modifications to one or more files that are
> > > +     different between the current branch and the branch to which
> > > +     you are switching, the command refuses to switch branches in
> > > +     order to preserve your modifications in context.  However,
> > > +     with this option, a three-way merge between the current
> > > +     branch, your working tree contents, and the new branch is
> > > +     done, and you will be on the new branch.
> >
> > I was wondering what people felt about making this the default for the
> > new command. If I'm carrying changes over to the new branch then I want
> > them to be merged in, it's annoying to have them carried over most of
> > the time but then sometimes have the checkout fail without specifying
> > --merge.
>
> If my worktree has local changes and I accidentally switch to a
> different branch, I could switch back without losing any local changes
> and the tree I have is exactly what I had before the switch. Is this
> still true if -m is made default?
>
> I think sometimes a 3-way merge creates marker conflicts in file, and
> this does not look easy to reverse when switching back. If it's true
> and we can detect it, we can still abort this case (i.e. requiring -m

You cannot yet do this with merge_recursive; it writes conflicts to
the worktree as it goes and doesn't tell you whether the merge was
successful or had conflicts until its done.  So this would be very
dangerous.  We'd first need a way to do an in-memory merge that
doesn't touch the working tree or index and which gives you the
opportunity to check whether that worked before proceeding to write
out any updates.  That is work I plan to do (for other reasons), but
not in progress currently.

> to continue) while allowing succesful 3-way merges by default. But are
> successful 3-way merges reversible?

Hmm, interesting.  So, switching from branch A to B, you start with
local changes on top of A that could represent a virtual commit C.
So, you three-way merge C & B using A as the base.  It's clean, so we
have a new endpoint, call it D.  Switching back to A with the --merge
flag would mean a three-way merge of D & A using B as the base.

But it might be easier if I re-labelled all of these with different
terms. Let's say we squash all changes from A to B into a single
commit on top of A that we call commit B.  Check out C (a commit
representing your local changes on top of A), and cherry-pick B; that
will three-way merge C & B using A as the base.  It's clean, so we
have a new commit, call it D.  Now we tell git to revert B, which will
do a three-way merge of D (current HEAD) & A (parent of B), using B as
the base.  So, essentially, your question about reversibility boils
down to: if you can cleanly cherry-pick a commit to your current
branch, can you then immediately revert that same commit on top to
return to where you were before?

(Off the cuff, I _think_ the answer to that is yes due to the
assumption that the cherry-pick was without conflicts, but I'm not
immediately sure.)

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14  5:59           ` Elijah Newren
@ 2019-03-14  7:23             ` Junio C Hamano
  2019-03-14 15:48               ` Elijah Newren
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-14  7:23 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Elijah Newren <newren@gmail.com> writes:

>> I think sometimes a 3-way merge creates marker conflicts in file, and
>> this does not look easy to reverse when switching back. If it's true
>> and we can detect it, we can still abort this case (i.e. requiring -m

Paths with actual conflicts are much easier to recover from than
paths that cleanly merge.   You have your original in stage #2, so
you should be able to "restore-path --stage=2 --from-stage path..."
them.  Once the contents are auto-resolved cleanly, however, the
cached contents are automaticaly updated to the auto-resolved result,
and it needs more work to reverse the effect of the merge (it is
doable, of course, as you know exactly the contents of the
switched-to branch and the contents of the switching-from branch, so
it is just the matter of running 3-way merge in the right direction
to recover what used to be in the working tree).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 17:28                     ` Eric Sunshine
  2019-03-13  1:55                       ` Junio C Hamano
@ 2019-03-14  9:17                       ` Duy Nguyen
  2019-03-14 11:02                         ` Phillip Wood
  2019-03-14 14:46                         ` Elijah Newren
  1 sibling, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-14  9:17 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Elijah Newren, Phillip Wood, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:
> > > Again, not much of a datapoint, but I do use --orphan periodically.
> > > The idea of "fixing" the behavior so that --orphan starts with a clean
> > > slate is certainly appealing (since it matches how I've used orphan
> > > branches in each case).
> >
> > The only three people who have commented on --orphan in this thread
> > all apparently feel the same way: the current behavior is wrong.
> > Maybe we can switch it to start with an empty index after all?
> 
> Starting empty may match intuition better. (More importantly, perhaps,
> it's harder to come up with a use-case for --orphan which doesn't
> involve starting with a clean slate.)

OK so the new --orphan description would be like this, right?

--8<--
--orphan <new-branch>::
	Create a new 'orphan' branch, named `<new-branch>`. If
	`<start-point>` is specified, the working tree is adjusted to
	match it. The index remains empty (i.e. no file is tracked).
-->8--

I was wondering if instead of the empty index, we mark on files from
<start-point> as intent-to-add. That way "git commit -a" achieves the
same as before, but you could still carefully craft the new index and
"git commit". Dunno. Not going to implement it unless somebody says
something, since I rarely (if ever?) use --orphan.

I may need someone to come up with a convincing commit message
too. All I've got is "I've been told this is a good thing to do" :)
--
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 17:05               ` Elijah Newren
@ 2019-03-14 10:42                 ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-14 10:42 UTC (permalink / raw)
  To: Elijah Newren, Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On 12/03/2019 17:05, Elijah Newren wrote:
> On Tue, Mar 12, 2019 at 4:58 AM Duy Nguyen <pclouds@gmail.com> wrote:
>> On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:
>>> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:
>>>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> 
>>>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
>>>>>> "git checkout" doing too many things is a source of confusion for many
>>>>>> users (and it even bites old timers sometimes). To remedy that, the
>>>>>> command will be split into two new ones: switch and
>>>>>> something-to-checkout-paths.
>>>>>
>>>>> I think this is a good idea, thanks for working on it. I wonder if it
>>>>> would be a good idea to have the new command refuse to checkout a new
>>>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with
>>>>> an option to override the check) as switching branches in the middle of
>>>>> one of those is likely to be confusing to users (if I do it it is
>>>>> normally because I've forgotten that I've not run 'git whatever
>>>>> --continue').
>>>>
>>>> Interesting. I think this would be a good default if we have an escape
>>>> hatch (which could even come later). I often wander off to some other
>>>> branch and go back. But then half the time I end up forgetting I'm in
>>>> a middle of something and just "git rebase --quit" :P
>>>>
>>>> Of course with git-stash (*) and git-worktree, I guess there's little
>>>> reason to just switch away from a something-in-progress worktree. I'll
>>>> try to implement this in the next round, unless someone objects.
>>>
>>> No objection here; I like this idea.
>>
>> One last thing. What about --detach? Should it have the same
>> protection or should we let the user doing --detach (experiments) take
>> the responsibity to not screw themselves up?
> 
> My intuition here is a bit weaker; it would be nice to hear others'
> opinions.  My best guess thinking it over a bit is that while someone
> doing --detach is more likely to know what they are doing than other
> users of the 'switch' command, they may also be the kind of person who
> is more likely to run interactive rebases and thus had more
> opportunities to forget that they are still in the middle of one.  I
> think I've been guilty of that a few times.  So, while this would be
> less critical for this case, I lean towards saying that it may still
> be helpful anyway, and if nothing else the consistency of handling all
> switching cases the same seems beneficial.
> I think that having all the switching cases consistent makes sense as it
is easy to understand and avoids complications if we end up adding a
config option to allow an implicit --detach in the future as suggested
in one of the later patches.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 16:43           ` Elijah Newren
@ 2019-03-14 11:00             ` Phillip Wood
  2019-03-19  9:39               ` [PATCH] checkout.txt: note about losing staged changes with --merge Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-14 11:00 UTC (permalink / raw)
  To: Elijah Newren, Phillip Wood
  Cc: Nguyễn Thái Ngọc Duy, Git Mailing List,
	Junio C Hamano, Eric Sunshine, SZEDER Gábor

On 12/03/2019 16:43, Elijah Newren wrote:
> On Tue, Mar 12, 2019 at 4:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> Hi Elijah
>>
>> On 11/03/2019 17:54, Elijah Newren wrote:
>>> A few other comments that I thought of while responding elsewhere in
>>> the thread that didn't make sense to include elsewhere...
>>>
>>> On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>>>
>>>> +-m::
>>>> +--merge::
>>>> +       If you have local modifications to one or more files that are
>>>> +       different between the current branch and the branch to which
>>>> +       you are switching, the command refuses to switch branches in
>>>> +       order to preserve your modifications in context.  However,
>>>> +       with this option, a three-way merge between the current
>>>> +       branch, your working tree contents, and the new branch is
>>>> +       done, and you will be on the new branch.
>>>> ++
>>>> +When a merge conflict happens, the index entries for conflicting
>>>> +paths are left unmerged, and you need to resolve the conflicts
>>>> +and mark the resolved paths with `git add` (or `git rm` if the merge
>>>> +should result in deletion of the path).
>>>
>>> Now that Phillip highlighted issues with -m and -f, it's hard not to
>>> wonder about other corner cases.  For example, what if the user made
>>> some changes, staged them, then made more changes, then tried to 'git
>>> checkout -m <other branch>'?  That's no longer a three-way merge, but
>>> four way.  How does that work?  Does it just rely on merge-recursive's
>>> (poorly defined) choice of when to bail out and when to permit such
>>> craziness?
>>
>> If the two-way merge fails then it does 'git add -u' before calling
>> merge_recursive(), then any merged paths are reset to the new HEAD
>> (which throws away newly added files, it should keep anything that is
>> not in HEAD or HEAD@{1}). So any staged changes are lost.
> 
> Ah, so roughly
>   * git add -u
>   * uncommitted_tree=$(git write-tree)
>   * git reset --hard
>   * git checkout $other_branch
>   * git merge-recursive $old_branch -- $other_branch $uncommitted_tree
>   * git reset --mixed HEAD

Something like that (I think it skips the reset and checkout and does
git merge-recursive $old_branch -- $uncommitted_tree $other_branch
and then updating HEAD)

> This at least gives well defined behavior, even if somewhat suboptimal
> in relation to losing staged changes (especially when those staged
> changes were new files).
> 
> I wonder if it'd be nicer, after I get my don't-touch-the-working-tree
> merge rewrite done, to instead do something like:
>   * Write the beginning index to a tree; call it $tree_0
>   * Note whether any working tree files differ from the index, add
> these all to a temporary index and write to to a tree; call it
> $tree_1.
>   * Do a three way in-memory merge of $old_branch with $other_branch
> and $tree_0; call it $merged_tree if there are no conflicts
>   * If $tree_0 == $tree_1, checkout the new branch and update the
> index and working tree to reflect the merge result.
>   * If $tree_0 != $tree_1 and there were any conflicts, abort telling
> the user they need to either unstage or stage changes first (we don't
> want to confuse users with a merge of a merge).
>   * Switch to the new branch, and update the index to match $merged_tree
>   * Do a three way in-memory merge of $old_branch with $merged_tree
> and $tree_1, writing the results (including any conflicts) to the
> working tree afterward.

As much as it annoys me to have to clear conflicts from the index after
a `checkout` or `stash pop` I'm wary of updating the working tree with
conflicts without marking those paths as unmerged in the index. Marking
them prevents the user from accidentally committing files with
unresolved conflicts. It is also easier for the user to find the
conflicts if they're marked as unmerged in the index, they can use `diff
--cc` and recreate them if they need to start over with the resolution.

> 
> Pros of this method:
>   * We don't lose newly staged files
>   * We don't lose user's carefully staged entries for existing files either
> Cons of this method:
>   * It may abort with an error if the user has a mix of both staged
> and unstaged changes (in particular, it will do so if the user's
> staged changes conflict with some difference in the new branch)

I think it's a good way of preserving any unstaged changes, it is
probably good that the user is warned ahead of time that their staged
changes would be lost.

> Thoughts?
> 
>>
>>>> +--orphan <new-branch>::
>>>> +       Create a new 'orphan' branch, named `<new-branch>`, started from
>>>> +       `<start-point>` and switch to it. See explanation of the same
>>>> +       option in linkgit:git-checkout[1] for details.
>>>
>>> Sigh...does this mean --orphan will remain broken?  It has always
>>> driven me crazy that it leaves you with a fully populated rather than
>>> an empty index.
>>
>> I've always thought that was weird.
>>
>>> It seemed broken to me before I figured out the
>>> special usecase,
>>
>> I haven't figured it out yet - what is it?
> 
> It's a presumption that despite the fact that you want a new branch,
> and one with no history to boot, that for some reason you want all the
> previous branch's current contents.  In particular, you can think of
> it as a way to squash all the history of an existing branch into a
> single commit in a new branch.
> 
> Knowing of this usecase doesn't make it bother me any less when I want
> to create a new unrelated empty branch; it seems like it took the
> esoteric usecase over the common one to me, but I'm biased.  It makes
> me feel better than neither you nor Eric could understand this
> behavior of --orphan either.

Thanks for the explanation, I agree it seems like a more esoteric use case.

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14  9:17                       ` Duy Nguyen
@ 2019-03-14 11:02                         ` Phillip Wood
  2019-03-14 12:56                           ` Duy Nguyen
  2019-03-14 14:46                         ` Elijah Newren
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-14 11:02 UTC (permalink / raw)
  To: Duy Nguyen, Eric Sunshine
  Cc: Elijah Newren, Phillip Wood, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On 14/03/2019 09:17, Duy Nguyen wrote:
> On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:
>>>> Again, not much of a datapoint, but I do use --orphan periodically.
>>>> The idea of "fixing" the behavior so that --orphan starts with a clean
>>>> slate is certainly appealing (since it matches how I've used orphan
>>>> branches in each case).
>>>
>>> The only three people who have commented on --orphan in this thread
>>> all apparently feel the same way: the current behavior is wrong.
>>> Maybe we can switch it to start with an empty index after all?
>>
>> Starting empty may match intuition better. (More importantly, perhaps,
>> it's harder to come up with a use-case for --orphan which doesn't
>> involve starting with a clean slate.)
> 
> OK so the new --orphan description would be like this, right?
> 
> --8<--
> --orphan <new-branch>::
> 	Create a new 'orphan' branch, named `<new-branch>`. If
> 	`<start-point>` is specified, the working tree is adjusted to
> 	match it. The index remains empty (i.e. no file is tracked).
> -->8--

What happens if no <start-point> is given? Do you end up with an empty
working tree or the current one? I'd lean towards an empty working tree
(with a check that there are no uncommitted changes, users can use
`restore` if they want some of the files back) but that is inconsistent
with the implicit <start-point> of -c.

Best Wishes

Phillip

> I was wondering if instead of the empty index, we mark on files from
> <start-point> as intent-to-add. That way "git commit -a" achieves the
> same as before, but you could still carefully craft the new index and
> "git commit". Dunno. Not going to implement it unless somebody says
> something, since I rarely (if ever?) use --orphan.
> 
> I may need someone to come up with a convincing commit message
> too. All I've got is "I've been told this is a good thing to do" :)
> --
> Duy
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14 11:02                         ` Phillip Wood
@ 2019-03-14 12:56                           ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-14 12:56 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Eric Sunshine, Elijah Newren, Phillip Wood, Git Mailing List,
	Junio C Hamano, SZEDER Gábor, Martin Ågren

On Thu, Mar 14, 2019 at 6:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 14/03/2019 09:17, Duy Nguyen wrote:
> > On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:
> >>>> Again, not much of a datapoint, but I do use --orphan periodically.
> >>>> The idea of "fixing" the behavior so that --orphan starts with a clean
> >>>> slate is certainly appealing (since it matches how I've used orphan
> >>>> branches in each case).
> >>>
> >>> The only three people who have commented on --orphan in this thread
> >>> all apparently feel the same way: the current behavior is wrong.
> >>> Maybe we can switch it to start with an empty index after all?
> >>
> >> Starting empty may match intuition better. (More importantly, perhaps,
> >> it's harder to come up with a use-case for --orphan which doesn't
> >> involve starting with a clean slate.)
> >
> > OK so the new --orphan description would be like this, right?
> >
> > --8<--
> > --orphan <new-branch>::
> >       Create a new 'orphan' branch, named `<new-branch>`. If
> >       `<start-point>` is specified, the working tree is adjusted to
> >       match it. The index remains empty (i.e. no file is tracked).
> > -->8--
>
> What happens if no <start-point> is given? Do you end up with an empty
> working tree or the current one? I'd lean towards an empty working tree
> (with a check that there are no uncommitted changes, users can use
> `restore` if they want some of the files back) but that is inconsistent
> with the implicit <start-point> of -c.

I was thinking default <start-point> is HEAD. But yeah empty tree
makes more sense since you can always say "switch --orphan <branch>
HEAD" but can't really say "give me an empty tree".
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:16       ` Phillip Wood
                           ` (2 preceding siblings ...)
  2019-03-14  4:39         ` Junio C Hamano
@ 2019-03-14 14:13         ` Duy Nguyen
  2019-03-17  6:00         ` [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index Nguyễn Thái Ngọc Duy
  2019-03-26 12:50         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
  5 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-14 14:13 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > +-f::
> > +--force::
> > +     Proceed even if the index or the working tree differs from
> > +     HEAD. Both the index and working tree are restored to match
> > +     the switching target. This is used to throw away local
> > +     changes.
>
> I'd always thought that --force meant "throw away my local changes if
> they conflict with the new branch" not "throw them away regardless"
> (which is better as it is deterministic). Maybe we can come up with a
> clearer name here --discard-changes? At the moment --force does not
> throw away conflicts properly (see the script below in my comments about
> --merge).

First victim of --discard-changes (or maybe I misread your comment),
it's too much to type even with completion and I'm so used to the
short and sweet "switch -[d]f".

Unless people object, I'm going to keep --force as an alias for
--discard-changes. -f could be extended later to cover more
--ignore-stuff when it makes sense.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14  9:17                       ` Duy Nguyen
  2019-03-14 11:02                         ` Phillip Wood
@ 2019-03-14 14:46                         ` Elijah Newren
  2019-03-18  2:03                           ` Junio C Hamano
  1 sibling, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-14 14:46 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Eric Sunshine, Phillip Wood, Git Mailing List, Junio C Hamano,
	SZEDER Gábor, Martin Ågren

On Thu, Mar 14, 2019 at 2:18 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:
> > > > Again, not much of a datapoint, but I do use --orphan periodically.
> > > > The idea of "fixing" the behavior so that --orphan starts with a clean
> > > > slate is certainly appealing (since it matches how I've used orphan
> > > > branches in each case).
> > >
> > > The only three people who have commented on --orphan in this thread
> > > all apparently feel the same way: the current behavior is wrong.
> > > Maybe we can switch it to start with an empty index after all?
> >
> > Starting empty may match intuition better. (More importantly, perhaps,
> > it's harder to come up with a use-case for --orphan which doesn't
> > involve starting with a clean slate.)
>
> OK so the new --orphan description would be like this, right?
>
> --8<--
> --orphan <new-branch>::
>         Create a new 'orphan' branch, named `<new-branch>`. If
>         `<start-point>` is specified, the working tree is adjusted to
>         match it. The index remains empty (i.e. no file is tracked).
> -->8--
>
> I was wondering if instead of the empty index, we mark on files from
> <start-point> as intent-to-add. That way "git commit -a" achieves the
> same as before, but you could still carefully craft the new index and
> "git commit". Dunno. Not going to implement it unless somebody says
> something, since I rarely (if ever?) use --orphan.

I don't see why <start-point> even makes sense to use with --orphan;
you should error if both are given, IMO.  The point of --orphan is to
create some entirely new history.  So, I'd expect "git switch --orphan
<new-branch>" to:
  * not create refs/heads/<new-branch>
  * set HEAD to refs/heads/<new-branch>
  * empty all tracked files from the working tree.
  * empty the index

Alternatively, you could allow <start-point> to be passed with
--orphan, adjusting the above steps so that both the index and the
working tree are switched to match <start-point>, but ONLY if
<start-point> defaults to the empty tree when --orphan is passed.

> I may need someone to come up with a convincing commit message
> too. All I've got is "I've been told this is a good thing to do" :)

How about:

"""
Switching and creating branches always involves knowing the
<start-point> to begin the new branch from.  Sometimes, people want to
create a new branch that does not have any commits yet; --orphan is a
flag to allow that.  --orphan overrides the default of HEAD for
<start-point> instead causing us to start from an empty history.  The
use of --orphan is incompatible with specifying a <start-point>.
"""

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14  7:23             ` Junio C Hamano
@ 2019-03-14 15:48               ` Elijah Newren
  0 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-14 15:48 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Thu, Mar 14, 2019 at 12:23 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> >> I think sometimes a 3-way merge creates marker conflicts in file, and
> >> this does not look easy to reverse when switching back. If it's true
> >> and we can detect it, we can still abort this case (i.e. requiring -m
>
> Paths with actual conflicts are much easier to recover from than
> paths that cleanly merge.   You have your original in stage #2, so
> you should be able to "restore-path --stage=2 --from-stage path..."
> them.  Once the contents are auto-resolved cleanly, however, the
> cached contents are automaticaly updated to the auto-resolved result,
> and it needs more work to reverse the effect of the merge (it is
> doable, of course, as you know exactly the contents of the
> switched-to branch and the contents of the switching-from branch, so
> it is just the matter of running 3-way merge in the right direction
> to recover what used to be in the working tree).

I agree that mixtures of conflicts and clean merges would be the most
difficult to reverse.  However, recovering from conflict cases is
actually harder than you mention, at least when renames are involved.
If foo is modified on both sides of history in conflicting ways AND
renamed to bar in the other branch, then all three staged will be
stored under the path bar.  Using your method to restore would cause
you to get a file named bar (though it would have the contents of the
original foo).

It can unfortunately get even worse.  With rename/rename(2to1)
conflicts; there are up to six values that needed to be shoved into
the normal three slots in the index, which merge-recursive achieves by
first content merging three at a time and shoving the (possibly
conflict-marker containing) results from that into two slots of the
index and then two-way merging from there to get the worktree contents
(possibly resulting in nested conflict markers).  Checking something
out of stage 2 thus not only might get the file path wrong but can
also get you contents with conflict markers.


However, this whole exercise gave me an idea that answers Duy's
original question definitively: you cannot necessarily reverse a
successful (i.e. no conflicts present) three-way merge with another
three-way merge.  Here's an example to demonstrate that...let's say
you are on branch A, and you have two identical files:
  A: foo, bar
and you want to switch to branch B which renamed foo but didn't modify it:
  B: baz, bar
locally on A you had renamed bar but didn't modify it:
  C: foo, baz
(C isn't an actual commit; I just wanted a label to refer to it)

doing a "git switch -m B" will result in merge-recursive noting that
both foo and bar were renamed to baz but that both versions of baz
where identical, so we'd end up with:
  D: baz
(D isn't an actual commit; I just wanted a label to refer to it)

Now, if we were to run "git switch -m A" to go back to A,
merge-recursive would need to do a three-way merge of D & A using B as
the base.  merge-recursive would note that A renamed baz->foo, and
that D deleted bar.  So, you'd end up with:
  E: foo

Unfortunately, C != E, so our reversing was unsuccessful.


In summary, this is yet another reason that making --merge the default
for either checkout or switch would be unsafe.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-12 16:51                   ` Elijah Newren
  2019-03-12 17:28                     ` Eric Sunshine
@ 2019-03-15  6:11                     ` Jacob Keller
  1 sibling, 0 replies; 289+ messages in thread
From: Jacob Keller @ 2019-03-15  6:11 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Eric Sunshine, Duy Nguyen, Phillip Wood, Git Mailing List,
	Junio C Hamano, SZEDER Gábor, Martin Ågren

On Tue, Mar 12, 2019 at 9:52 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> >
> > On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > > On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > > > I tend to agree with this but that's probably because I don't really use
> > > > checkout -B. I'm not sure if it's widely used or not. I do find checkout
> > > > -b convenient though.
> > >
> > > Yeah I think both -b and -B are about convenience.
> > >
> > > But I would not mind dropping -C for now, if people think it's not
> > > that useful. We can bring it back in incremental updates if we realize
> > > we miss it so much. I'll keep it unless somebody says something.
> >
> > It's not much of a datapoint, but I do use "git checkout -B" (and
> > therefore would use "git switch -C") periodically (in addition to
> > -b/-c, which I use all the time). And, convenience is important,
> > especially considering that "git switch" is already more painful in
> > some ways than "git checkout", due to having to trigger and spell out
> > certain things explicitly (such as detaching).
>
> Ooh, interesting.  I haven't used it and didn't know who did, but
> since you do you can probably answer the question surrounding the
> long-name for the -C option from earlier in the thread:
>
> Do you use checkout -B only when checkout -b fails, or do you use it
> pre-emptively?  The former would suggest we should use a name like
> --recreate, while the latter would suggest a name more like
> --force-create.

I use it periodically sometimes like this:

git checkout -B master origin/master

Essentially, I use it as a way to quickly and forcefully re-create a
branch that tracks the remote branch.

For example, I might have made a commit or two on master, and realized
I should be doing a separate branch, and I want to quickly make that a
branch by doing

git checkout -b some-branch.

Then, later I want to switch back to master, and I really just want
master to be exactly what the remote had. Sure I can do that in a lot
of ways, but it's nice to have a somewhat convenient shortcut to do it
in one command.

So, for me, it's natural to think of this as a "--force-create", but
use of either --recreate or --force- create work with my setup.

Basically, for *me*, I almost always use it when I know there's a
branch that I want to re-setup there, and rarely verify that fact
using a "oh, -b failed, I guess I need -B"

Regards,
Jake

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-13 18:36       ` Eckhard Maaß
@ 2019-03-15  8:19         ` Eric Sunshine
  2019-03-15  9:29           ` Duy Nguyen
  2019-03-18  4:11           ` Junio C Hamano
  2019-03-16  3:59         ` Duy Nguyen
  1 sibling, 2 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-15  8:19 UTC (permalink / raw)
  To: Eckhard Maaß
  Cc: Nguyễn Thái Ngọc Duy, Git List, Junio C Hamano,
	SZEDER Gábor

On Wed, Mar 13, 2019 at 2:36 PM Eckhard Maaß
<eckhard.s.maass@googlemail.com> wrote:
> On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > Similar to automatic detach, this behavior could be confusing because
> > it can sometimes create a new branch without a user asking it to,
> > especially when the user is still not aware about this feature.
> >
> > In the future, perhaps we could have a config key to disable these
> > safety nets and let 'switch' do automatic detach or dwim
> > again. But that will be opt-in after the user knows what is what. For
> > now give a short option if you want to use it often.
>
> As I am late to the patch series (sorry!), has there been already any
> discussion on that? In my experience, people get confused with detached
> HEAD state quite often, whereas the automatic creation of a local branch
> is no problem.

This statement does a good job of articulating my (unspoken) response
to this patch. Whereas a detached HEAD might be scary and confusing to
newcomers, and difficult for them to recover from, automatic creation
of a DWIM'd local branch doesn't seem so problematic (if at all).

With git-checkout, it's very easy to accidentally get into a detached
HEAD state, so it makes some sense to protect newcomers, by default,
from that accident in git-switch. However, auto-creation of a new
local branch is not, for a couple reasons, nearly so weighty a matter.
First, in many cases it may be less likely to happen since it requires
presence of a corresponding remote tracking branch. Second, it's
intuitively easy to recover from it: when git-switch reports that it
created a new branch, though perhaps surprising, the user would
naturally know to look for a command to "delete a branch".

And, unlike a detached HEAD, which newcomers may mistakenly believe
lead to irretrievable loss of work, an unexpected branch creation
carries no such penalty, perceived or real.

> So if it is deemed to be too confusing to dwim in this
> case, could we add a hint suggesting the command? Something like:
> "No suitable branch <foo> found, however there is a remote tracking
> branch <origin/foo> that you can siwtch and create with `git switch
> --guess foo`" (or maybe the one without guess)?

That could be helpful if git-switch continues to make --no-guess the
default, but making --guess the default (as it is in git-checkout)
would likely be even better (for the reasons enumerated above).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-15  8:19         ` Eric Sunshine
@ 2019-03-15  9:29           ` Duy Nguyen
  2019-03-18  4:11           ` Junio C Hamano
  1 sibling, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-15  9:29 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Eckhard Maaß, Git List, Junio C Hamano, SZEDER Gábor

On Fri, Mar 15, 2019 at 3:19 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Wed, Mar 13, 2019 at 2:36 PM Eckhard Maaß
> <eckhard.s.maass@googlemail.com> wrote:
> > On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > > Similar to automatic detach, this behavior could be confusing because
> > > it can sometimes create a new branch without a user asking it to,
> > > especially when the user is still not aware about this feature.
> > >
> > > In the future, perhaps we could have a config key to disable these
> > > safety nets and let 'switch' do automatic detach or dwim
> > > again. But that will be opt-in after the user knows what is what. For
> > > now give a short option if you want to use it often.
> >
> > As I am late to the patch series (sorry!), has there been already any
> > discussion on that? In my experience, people get confused with detached
> > HEAD state quite often, whereas the automatic creation of a local branch
> > is no problem.
>
> This statement does a good job of articulating my (unspoken) response
> to this patch. Whereas a detached HEAD might be scary and confusing to
> newcomers, and difficult for them to recover from, automatic creation
> of a DWIM'd local branch doesn't seem so problematic (if at all).
>
> With git-checkout, it's very easy to accidentally get into a detached
> HEAD state, so it makes some sense to protect newcomers, by default,
> from that accident in git-switch. However, auto-creation of a new
> local branch is not, for a couple reasons, nearly so weighty a matter.
> First, in many cases it may be less likely to happen since it requires
> presence of a corresponding remote tracking branch. Second, it's
> intuitively easy to recover from it: when git-switch reports that it
> created a new branch, though perhaps surprising, the user would
> naturally know to look for a command to "delete a branch".
>
> And, unlike a detached HEAD, which newcomers may mistakenly believe
> lead to irretrievable loss of work, an unexpected branch creation
> carries no such penalty, perceived or real.

I can't remember the last time it was discussed, but part of the
reasons I chose to default --no-guess is because completion will be a
lot less noisy.

But that's a very personal preference. I will switch to --guess as
default no problem (unless someone jumps in and screams NOOO of
course).

Please don't hold back when you find something not quite right. At
least now I can fix it. Either that or by the time it's released, the
Internet will blame me for adding yet another confusing git command :P
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-13 18:36       ` Eckhard Maaß
  2019-03-15  8:19         ` Eric Sunshine
@ 2019-03-16  3:59         ` Duy Nguyen
  1 sibling, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-16  3:59 UTC (permalink / raw)
  To: Eckhard Maaß
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine, SZEDER Gábor

On Thu, Mar 14, 2019 at 1:36 AM Eckhard Maaß
<eckhard.s.maass@googlemail.com> wrote:
> And while at it - what should happen, if:
>
> - there is a tag named example
> - no local branch example
> - a branch at origin/example
>
> ... and we switch then? Right now it just gives "cannot find branch",
> should there be more information? Should it even create a branch
> example? With switch, switching a branch is unambiguous, even though
> there is a tag with that name. If I really want to --guess - should I be
> given more information?

There's checkout.defaultRemote that changes this behavior and it
affects git-switch as well.

I like the idea of giving more information than just the terse (and
technically correct) "cannot find branch". But I may have to delay
implementing this a bit. There's a been a lot of updates in git-switch
(which is great, don't get me wrong, I appreciate the feedback). I
need to get v4 out for review soon then maybe handle the rest in v5
(and finally take a real hard look at git-restore comments).
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-11 11:16       ` Phillip Wood
                           ` (3 preceding siblings ...)
  2019-03-14 14:13         ` Duy Nguyen
@ 2019-03-17  6:00         ` Nguyễn Thái Ngọc Duy
  2019-03-18  3:58           ` Junio C Hamano
  2019-03-18 11:38           ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  2019-03-26 12:50         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
  5 siblings, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17  6:00 UTC (permalink / raw)
  To: phillip.wood123, gitster
  Cc: git, martin.agren, pclouds, sunshine, szeder.dev, Elijah Newren

One-way merge is supposed to take stat info from the index and
everything else from the given tree. This implies stage 0 because trees
can't have non-zero stages. The add_entry(.., old, ...) call however
will keep stage index from the index.

This is normally not a problem if the entry from the index is
normal (stage #0). But if there is a conflict, we'll get stage #1 entry
as "old" and it gets recorded in the final index. Fix it by clearing
stage mask.

This bug probably comes from b5b425074e (git-read-tree: make one-way
merge also honor the "update" flag, 2005-06-07). Before this commit, we
may create the final ("dst") index entry from the one in index, but we
do clear CE_STAGEMASK.

I briefly checked two- and three-way merge functions. I think we don't
have the same problem in those.

Reported-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 This is one of the two bugs reported by Phillip. It's not tangled with
 nd/switch-and-restore code changes and I'm sending it separately.

 t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++
 unpack-trees.c                     |  2 +-
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/t/t2026-checkout-force.sh b/t/t2026-checkout-force.sh
new file mode 100755
index 0000000000..272ccf533a
--- /dev/null
+++ b/t/t2026-checkout-force.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='checkout --force'
+. ./test-lib.sh
+
+test_expect_success 'force checking out a conflict' '
+	echo a >a &&
+	git add a &&
+	git commit -ama &&
+	A_OBJ=$(git rev-parse :a) &&
+	git branch topic &&
+	echo b >a &&
+	git commit -amb &&
+	B_OBJ=$(git rev-parse :a) &&
+	git checkout topic &&
+	echo c >a &&
+	C_OBJ=$(git hash-object a) &&
+	git checkout -m master &&
+	test_cmp_rev :1:a $A_OBJ &&
+	test_cmp_rev :2:a $B_OBJ &&
+	test_cmp_rev :3:a $C_OBJ &&
+	git checkout -f topic &&
+	test_cmp_rev :a $A_OBJ
+'
+
+test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 22c41a3ba8..1ccd343cad 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,
 		if (o->update && S_ISGITLINK(old->ce_mode) &&
 		    should_update_submodules() && !verify_uptodate(old, o))
 			update |= CE_UPDATE;
-		add_entry(o, old, update, 0);
+		add_entry(o, old, update, CE_STAGEMASK);
 		return 0;
 	}
 	return merged_entry(a, old, o);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 00/26] Add new command 'switch'
  2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
                       ` (21 preceding siblings ...)
  2019-03-08 17:48     ` [PATCH v3 00/21] Add new command "switch" Ramsay Jones
@ 2019-03-17 12:49     ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
                         ` (27 more replies)
  22 siblings, 28 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Thanks for all the comments from v3 (and before), I didn't expect
feedback from so many people. v4 fixes most of them, but still leaves
a couple for v5.

- -C remains because people seem to need it

- --recreate vs --force-create: there was no strong preference for
  either, so I stick with --force-create

- --guess (dwim mode) is now made default

- --orphan by default switches to an empty tree instead of current HEAD
  (this is Elijah's alternative approach). This allows you to start
  fresh, but you can still keep "HEAD" content or even switch to another
  tree.

- advice.detachedHead text is updated to suggest a way out (currently it
  only suggests a way forward with 'checkout -b')

- --force has a new alias --discard-changes

- the error message when we expect a branch is improved to help point
  out why the given argument is not a branch

- more docs update and reorganization, and other silly bugs I made in
  v3

The two things I'm aware of but have not done:

- the bug in 'git checkout -m' and 'git switch -m' that could lead to
  loss of staged changes.

- better suggestion when dwim fails (e.g. when there are multiple dwim
  candidates, we could show them all).

There have been lots of discussions back and forth. If I miss anything
else, please let me know.

Nguyễn Thái Ngọc Duy (26):
  git-checkout.txt: spell out --no-option
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  git-checkout.txt: fix monospace typeset
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: advice how to get out of detached HEAD mode
  checkout: keep most #include sorted
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command 'switch'
  switch: better names for -b and -B
  switch: add --discard-changes
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: implicit dwim, use --no-guess to disable it
  switch: no worktree status unless real branch switch happens
  switch: reject if some operation is in progress
  switch: --orphan defaults to empty tree as HEAD
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |  12 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                | 221 ++++---
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   5 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt (new)            | 277 ++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  19 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/gitworkflows.txt                |   3 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  56 +-
 Makefile                                      |   1 +
 advice.c                                      |  17 +-
 builtin.h                                     |   1 +
 builtin/checkout.c                            | 597 ++++++++++--------
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  37 +-
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   1 +
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |  28 +-
 t/t2060-switch.sh (new +x)                    |  97 +++
 39 files changed, 1043 insertions(+), 493 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh

Range-diff dựa trên v3:
 1:  b0cb2372db !  1:  535dc1f310 doc: document --overwrite-ignore
    @@ -22,7 +22,20 @@
     +
      --recurse-submodules::
      --no-recurse-submodules::
    - 	Using --recurse-submodules will update the content of all initialized
    +-	Using --recurse-submodules will update the content of all initialized
    ++	Using `--recurse-submodules` will update the content of all initialized
    + 	submodules according to the commit recorded in the superproject. If
    + 	local modifications in a submodule would be overwritten the checkout
    +-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
    ++	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
    + 	is used, the work trees of submodules will not be updated.
    +-	Just like linkgit:git-submodule[1], this will detach the
    +-	submodules HEAD.
    ++	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    ++	submodule.
    + 
    + --no-guess::
    + 	Do not attempt to create a branch if a remote tracking branch
     
      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
      --- a/Documentation/git-merge.txt
 2:  5b165524d1 <  -:  ---------- git-checkout.txt: fix monospace typeset
 -:  ---------- >  2:  b6305d2871 git-checkout.txt: fix monospace typeset
 3:  a4240a888e =  3:  bb56e45457 t: rename t2014-switch.sh to t2014-checkout-switch.sh
 -:  ---------- >  4:  123392757a checkout: advice how to get out of detached HEAD mode
 -:  ---------- >  5:  d1ec6b4ce0 checkout: keep most #include sorted
 4:  dc4a8e8933 =  6:  4b1742672b checkout: factor out some code in parse_branchname_arg()
 5:  f0d933c2ac =  7:  e0bcc3a4dd checkout: make "opts" in cmd_checkout() a pointer
 6:  0aa541689c =  8:  ca5b4d7db0 checkout: move 'confict_style' and 'dwim_..' to checkout_opts
 7:  84ca42bb26 =  9:  13c03997f0 checkout: split options[] array in three pieces
 8:  e983c8bb2c ! 10:  24d532b276 checkout: split part of it to new command 'switch'
    @@ -4,9 +4,9 @@
     
         "git checkout" doing too many things is a source of confusion for many
         users (and it even bites old timers sometimes). To remedy that, the
    -    command will be split into two new ones: switch and
    -    something-to-checkout-paths. The good old "git checkout" command is
    -    still here and will be until all (or most of users) are sick of it.
    +    command will be split into two new ones: switch and restore. The good
    +    old "git checkout" command is still here and will be until all (or most
    +    of users) are sick of it.
     
         See the new man page for the final design of switch. The actual
         implementation though is still pretty much the same as "git checkout"
    @@ -157,12 +157,13 @@
     +updated to match the branch. All new commits will be added to the tip
     +of this branch.
     +
    -+Optionally a new branch could be created with either `-c` or `-C`, or
    ++Optionally a new branch could be created with either `-c`, `-C`,
    ++automatically from a remote branch of same name (see `--guess`), or
     +detach the working tree from any branch with `--detach`, along with
     +switching.
     +
     +Switching branches does not require a clean index and working tree
    -+(i.e. no differences compared to 'HEAD'). The operation is aborted
    ++(i.e. no differences compared to `HEAD`). The operation is aborted
     +however if the switch leads to loss of local changes, unless told
     +otherwise.
     +
    @@ -175,14 +176,19 @@
     +	Name for the new branch.
     +
     +<start-point>::
    -+	The name of a commit at which to switch to before creating a
    -+	new branch or detach from.
    ++	The starting point for the new branch. Specifying a
    ++	`<start-point>` allows you to create a branch based on some
    ++	other point in history than where HEAD currently points. (Or,
    ++	in the case of `--detach`, allows you to inspect and detach
    ++	from some other point.)
     ++
    -+You can use the `"@{-N}"` syntax to refer to the N-th last
    ++You can use the `@{-N}` syntax to refer to the N-th last
     +branch/commit switched to "git switch" or "git checkout"
    -+operation. You may also specify `-` which is synonymous to `"@{-1}"`.
    ++operation. You may also specify `-` which is synonymous to `@{-1}`.
    ++This is often used to switch quickly between two branches, or to undo
    ++a branch switch by mistake.
     ++
    -+As a special case, you may use `"A...B"` as a shortcut for the merge
    ++As a special case, you may use `A...B` as a shortcut for the merge
     +base of `A` and `B` if there is exactly one merge base. You can leave
     +out at most one of `A` and `B`, in which case it defaults to `HEAD`.
     +
    @@ -214,8 +220,8 @@
     +	experiments. See the "DETACHED HEAD" section in
     +	linkgit:git-checkout[1] for details.
     +
    -+-g::
     +--guess::
    ++--no-guess::
     +	If `<branch>` is not found but there does exist a tracking
     +	branch in exactly one remote (call it `<remote>`) with a
     +	matching name, treat as equivalent to
    @@ -231,18 +237,45 @@
     +to always checkout remote branches from there if `<branch>` is
     +ambiguous but exists on the 'origin' remote. See also
     +`checkout.defaultRemote` in linkgit:git-config[1].
    -+
    -+-q::
    -+--quiet::
    -+	Quiet, suppress feedback messages.
    +++
    ++`--guess` is the default behavior. Use `--no-guess` to disable it.
     +
     +-f::
     +--force::
    ++	An alias for `--discard-changes`.
    ++
    ++--discard-changes::
     +	Proceed even if the index or the working tree differs from
    -+	HEAD. Both the index and working tree are restored to match
    ++	`HEAD`. Both the index and working tree are restored to match
     +	the switching target. This is used to throw away local
     +	changes.
     +
    ++-m::
    ++--merge::
    ++	If you have local modifications to one or more files that are
    ++	different between the current branch and the branch to which
    ++	you are switching, the command refuses to switch branches in
    ++	order to preserve your modifications in context.  However,
    ++	with this option, a three-way merge between the current
    ++	branch, your working tree contents, and the new branch is
    ++	done, and you will be on the new branch.
    +++
    ++When a merge conflict happens, the index entries for conflicting
    ++paths are left unmerged, and you need to resolve the conflicts
    ++and mark the resolved paths with `git add` (or `git rm` if the merge
    ++should result in deletion of the path).
    ++
    ++--conflict=<style>::
    ++	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).
    ++
    ++-q::
    ++--quiet::
    ++	Quiet, suppress feedback messages.
    ++
     +--progress::
     +--no-progress::
     +	Progress status is reported on the standard error stream
    @@ -253,49 +286,28 @@
     +-t::
     +--track::
     +	When creating a new branch, set up "upstream" configuration.
    -+	`-c` is implied. See "--track" in linkgit:git-branch[1] for
    ++	`-c` is implied. See `--track` in linkgit:git-branch[1] for
     +	details.
     ++
     +If no `-c` option is given, the name of the new branch will be derived
     +from the remote-tracking branch, by looking at the local part of the
     +refspec configured for the corresponding remote, and then stripping
    -+the initial part up to the "*".  This would tell us to use "hack" as
    -+the local branch when branching off of "origin/hack" (or
    -+"remotes/origin/hack", or even "refs/remotes/origin/hack").  If the
    ++the initial part up to the "*".  This would tell us to use `hack` as
    ++the local branch when branching off of `origin/hack` (or
    ++`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the
     +given name has no slash, or the above guessing results in an empty
     +name, the guessing is aborted.  You can explicitly give a name with
     +`-c` in such a case.
     +
     +--no-track::
     +	Do not set up "upstream" configuration, even if the
    -+	branch.autoSetupMerge configuration variable is true.
    -+
    -+-m::
    -+--merge::
    -+	If you have local modifications to one or more files that are
    -+	different between the current branch and the branch to which
    -+	you are switching, the command refuses to switch branches in
    -+	order to preserve your modifications in context.  However,
    -+	with this option, a three-way merge between the current
    -+	branch, your working tree contents, and the new branch is
    -+	done, and you will be on the new branch.
    -++
    -+When a merge conflict happens, the index entries for conflicting
    -+paths are left unmerged, and you need to resolve the conflicts
    -+and mark the resolved paths with `git add` (or `git rm` if the merge
    -+should result in deletion of the path).
    -+
    -+--conflict=<style>::
    -+	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).
    ++	`branch.autoSetupMerge` configuration variable is true.
     +
     +--orphan <new-branch>::
    -+	Create a new 'orphan' branch, named `<new-branch>`, started from
    -+	`<start-point>` and switch to it. See explanation of the same
    -+	option in linkgit:git-checkout[1] for details.
    ++	Create a new 'orphan' branch, named `<new-branch>`. If
    ++	`<start-point>` is specified, the index and working tree are
    ++	adjusted to match it. Otherwise both are adjusted to contain no
    ++	tracked files.
     +
     +--ignore-other-worktrees::
     +	`git switch` refuses when the wanted ref is already
    @@ -303,15 +315,20 @@
     +	the ref out anyway. In other words, the ref can be held by
     +	more than one worktree.
     +
    ++--ignore-in-progress::
    ++	`git switch` by default refuses when some operation is in
    ++	progress (e.g. "git rebase", "git am" ...). This option
    ++	overrides this safety check and allows switching.
    ++
     +--recurse-submodules::
     +--no-recurse-submodules::
    -+	Using --recurse-submodules will update the content of all initialized
    ++	Using `--recurse-submodules` will update the content of all initialized
     +	submodules according to the commit recorded in the superproject. If
     +	local modifications in a submodule would be overwritten the checkout
    -+	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
    ++	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
     +	is used, the work trees of submodules will not be updated.
    -+	Just like linkgit:git-submodule[1], this will detach the
    -+	submodules HEAD.
    ++	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    ++	submodule.
     +
     +EXAMPLES
     +--------
    @@ -366,7 +383,7 @@
     +------------
     +
     +If you want to start a new branch from a remote branch of the same
    -+name, use `-g`:
    ++name:
     +
     +------------
     +$ git switch -g new-topic
    @@ -374,7 +391,7 @@
     +Switched to a new branch 'new-topic'
     +------------
     +
    -+To check out commit "HEAD~3" for temporary inspection or experiment
    ++To check out commit `HEAD~3` for temporary inspection or experiment
     +without creating a new branch:
     +
     +------------
    @@ -391,7 +408,8 @@
     +
     +SEE ALSO
     +--------
    -+linkgit:git-checkout[1]
    ++linkgit:git-checkout[1],
    ++linkgit:git-branch[1]
     +
     +GIT
     +---
 9:  5491932cc8 = 11:  c966bacfcc switch: better names for -b and -B
 -:  ---------- > 12:  bdb88bf9a9 switch: add --discard-changes
10:  e0f1247b09 = 13:  d5fe7f4bd0 switch: remove -l
11:  f7ff1a7df6 ! 14:  3bce4c521e switch: stop accepting pathspec
    @@ -11,9 +11,9 @@
      --- a/builtin/checkout.c
      +++ b/builtin/checkout.c
     @@
    - 	int count_checkout_paths;
      	int overlay_mode;
      	int no_dwim_new_local_branch;
    + 	int discard_changes;
     +	int accept_pathspec;
      
      	/*
12:  0750d63d38 ! 15:  dad0063fc4 switch: reject "do nothing" case
    @@ -17,8 +17,8 @@
      --- a/builtin/checkout.c
      +++ b/builtin/checkout.c
     @@
    - 	int overlay_mode;
      	int no_dwim_new_local_branch;
    + 	int discard_changes;
      	int accept_pathspec;
     +	int switch_branch_doing_nothing_is_ok;
      
13:  bcd843146d ! 16:  41ca042917 switch: only allow explicit detached HEAD
    @@ -24,6 +24,36 @@
      	int writeout_stage;
      	int overwrite_ignore;
      	int ignore_skipworktree;
    +@@
    + 	return status;
    + }
    + 
    ++static void die_expecting_a_branch(const struct branch_info *branch_info)
    ++{
    ++	struct object_id oid;
    ++	char *to_free;
    ++
    ++	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
    ++		const char *ref = to_free;
    ++
    ++		if (skip_prefix(ref, "refs/tags/", &ref))
    ++			die(_("a branch is expected, got tag '%s'"), ref);
    ++		if (skip_prefix(ref, "refs/remotes/", &ref))
    ++			die(_("a branch is expected, got remote branch '%s'"), ref);
    ++		die(_("a branch is expected, got '%s'"), ref);
    ++	}
    ++	if (branch_info->commit)
    ++		die(_("a branch is expected, got commit '%s'"), branch_info->name);
    ++	/*
    ++	 * This case should never happen because we already die() on
    ++	 * non-commit, but just in case.
    ++	 */
    ++	die(_("a branch is expected, got '%s'"), branch_info->name);
    ++}
    ++
    + static int checkout_branch(struct checkout_opts *opts,
    + 			   struct branch_info *new_branch_info)
    + {
     @@
      	    !opts->force_detach)
      		die(_("missing branch or commit argument"));
    @@ -34,7 +64,7 @@
     +	    !opts->new_branch_force &&
     +	    new_branch_info->name &&
     +	    !new_branch_info->path)
    -+		die(_("a branch is expected, got %s"), new_branch_info->name);
    ++		die_expecting_a_branch(new_branch_info);
     +
      	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
      	    !opts->ignore_other_worktrees) {
14:  d5cc55525d = 17:  a0b9f1b285 switch: add short option for --detach
15:  4a27400a58 ! 18:  3d254df104 switch: no implicit dwim, use --guess to dwim
    @@ -1,15 +1,15 @@
     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
     
    -    switch: no implicit dwim, use --guess to dwim
    +    switch: implicit dwim, use --no-guess to disable it
     
    -    Similar to automatic detach, this behavior could be confusing because
    -    it can sometimes create a new branch without a user asking it to,
    -    especially when the user is still not aware about this feature.
    +    This is already the default in git-checkout. The real change in here is
    +    just minor cleanup. The main excuse is to explain why dwim is kept default.
     
    -    In the future, perhaps we could have a config key to disable these
    -    safety nets and let 'switch' do automatic detach or dwim
    -    again. But that will be opt-in after the user knows what is what. For
    -    now give a short option if you want to use it often.
    +    Contrary to detach mode that is easy to get into and confusing to get
    +    back out. Automatically creating a tracking branch often does not kick
    +    in as often (you would need a branch of the same name on a remote). And
    +    since the branch creation is reported clearly, the user should be able
    +    to undo/delete it if it's unwanted.
     
      diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
      --- a/Documentation/git-checkout.txt
    @@ -41,7 +41,7 @@
      rather expensive side-effects to show only the tracking information,
     @@
      	Do not set up "upstream" configuration, even if the
    - 	branch.autoSetupMerge configuration variable is true.
    + 	`branch.autoSetupMerge` configuration variable is true.
      
     +--guess::
     +--no-guess::
    @@ -68,8 +68,8 @@
      	Create the new branch's reflog; see linkgit:git-branch[1] for
      	details.
     @@
    - 	Just like linkgit:git-submodule[1], this will detach the
    - 	submodules HEAD.
    + 	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    + 	submodule.
      
     ---no-guess::
     -	Do not attempt to create a branch if a remote tracking branch
    @@ -88,9 +88,9 @@
      	int overlay_mode;
     -	int no_dwim_new_local_branch;
     +	int dwim_new_local_branch;
    + 	int discard_changes;
      	int accept_pathspec;
      	int switch_branch_doing_nothing_is_ok;
    - 
     @@
      		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
      			   N_("update ignored files (default)"),
    @@ -145,15 +145,17 @@
      			   N_("create and switch to a new branch")),
      		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
      			   N_("create/reset and switch to a branch")),
    -+		OPT_BOOL('g', "guess", &opts.dwim_new_local_branch,
    ++		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
     +			 N_("second guess 'git switch <no-such-branch>'")),
    + 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
    + 			 N_("throw away local modifications")),
      		OPT_END()
    - 	};
    +@@
      	int ret;
      
      	memset(&opts, 0, sizeof(opts));
     -	opts.no_dwim_new_local_branch = 0;
    -+	opts.dwim_new_local_branch = 0;
    ++	opts.dwim_new_local_branch = 1;
      	opts.accept_pathspec = 0;
      	opts.switch_branch_doing_nothing_is_ok = 0;
      	opts.implicit_detach = 0;
16:  f7dd33abe6 ! 19:  c6ea203f36 switch: no worktree status unless real branch switch happens
    @@ -36,8 +36,8 @@
      --- a/builtin/checkout.c
      +++ b/builtin/checkout.c
     @@
    - #include "submodule.h"
    - #include "advice.h"
    + #include "unpack-trees.h"
    + #include "xdiff-interface.h"
      
     -static int checkout_optimize_new_branch;
     -
    @@ -45,7 +45,7 @@
      	N_("git checkout [<options>] <branch>"),
      	N_("git checkout [<options>] [<branch>] -- <file>..."),
     @@
    - 	int dwim_new_local_branch;
    + 	int discard_changes;
      	int accept_pathspec;
      	int switch_branch_doing_nothing_is_ok;
     -
    @@ -220,7 +220,7 @@
      	opts.implicit_detach = 1;
      
     @@
    - 	opts.dwim_new_local_branch = 0;
    + 	opts.dwim_new_local_branch = 1;
      	opts.accept_pathspec = 0;
      	opts.switch_branch_doing_nothing_is_ok = 0;
     +	opts.only_merge_on_switching_branches = 1;
 -:  ---------- > 20:  5c4effc7fd switch: reject if some operation is in progress
 -:  ---------- > 21:  a4afe6a999 switch: --orphan defaults to empty tree as HEAD
17:  5b182abea8 ! 22:  6cca78f835 t: add tests for switch
    @@ -31,12 +31,9 @@
     +	test_path_is_missing second.t
     +'
     +
    -+test_expect_success 'switch to a commit' '
    -+	test_must_fail git switch master^{commit}
    -+'
    -+
     +test_expect_success 'switch and detach' '
     +	test_when_finished git switch master &&
    ++	test_must_fail git switch master^{commit} &&
     +	git switch --detach master^{commit} &&
     +	test_must_fail git symbolic-ref HEAD
     +'
    @@ -60,6 +57,7 @@
     +test_expect_success 'force create branch from HEAD' '
     +	test_when_finished git switch master &&
     +	git switch --detach master &&
    ++	test_must_fail git switch -c temp &&
     +	git switch -C temp &&
     +	test_cmp_rev master refs/heads/temp &&
     +	echo refs/heads/temp >expected-branch &&
    @@ -67,12 +65,15 @@
     +	test_cmp expected-branch actual-branch
     +'
     +
    -+test_expect_success 'new orphan branch' '
    ++test_expect_success 'new orphan branch from empty' '
     +	test_when_finished git switch master &&
    -+	git switch --orphan new-orphan master^ &&
    ++	git switch --orphan new-orphan &&
     +	test_commit orphan &&
     +	git cat-file commit refs/heads/new-orphan >commit &&
    -+	! grep ^parent commit
    ++	! grep ^parent commit &&
    ++	git ls-files >tracked-files &&
    ++	echo orphan.t >expected &&
    ++	test_cmp expected tracked-files
     +'
     +
     +test_expect_success 'switching ignores file of same branch name' '
    @@ -81,16 +82,25 @@
     +	git switch first-branch &&
     +	echo refs/heads/first-branch >expected &&
     +	git symbolic-ref HEAD >actual &&
    -+	test_commit expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'guess and create branch ' '
     +	test_when_finished git switch master &&
    -+	test_must_fail git switch foo &&
    -+	git switch --guess foo &&
    ++	test_must_fail git switch --no-guess foo &&
    ++	git switch foo &&
     +	echo refs/heads/foo >expected &&
     +	git symbolic-ref HEAD >actual &&
     +	test_cmp expected actual
     +'
     +
    ++test_expect_success 'switching when something is in progress' '
    ++	test_when_finished rm -f .git/MERGE_HEAD &&
    ++	# fake a merge-in-progress
    ++	cp .git/HEAD .git/MERGE_HEAD &&
    ++	test_must_fail git switch -d @^ &&
    ++	git switch --ignore-in-progress -d @^ &&
    ++	git switch -
    ++'
    ++
     +test_done
18:  1575064fe6 ! 23:  e5e6f9d6f1 completion: support switch
    @@ -10,6 +10,16 @@
      diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
      --- a/contrib/completion/git-completion.bash
      +++ b/contrib/completion/git-completion.bash
    +@@
    + #   GIT_COMPLETION_CHECKOUT_NO_GUESS
    + #
    + #     When set to "1", do not include "DWIM" suggestions in git-checkout
    +-#     completion (e.g., completing "foo" when "origin/foo" exists).
    ++#     and git-switch completion (e.g., completing "foo" when "origin/foo"
    ++#     exists).
    + 
    + case "$COMP_WORDBREAKS" in
    + *:*) : great ;;
     @@
      	__git_complete_index_file "$complete_opt"
      }
    @@ -24,15 +34,22 @@
     +		__gitcomp_builtin switch
     +		;;
     +	*)
    -+		# check if ---guess was specified to enable DWIM mode
    -+		local track_opt= only_local_ref=n
    -+		if [ -n "$(__git_find_on_cmdline "-g --guess")" ]; then
    ++		# check if --track, --no-track, or --no-guess was specified
    ++		# if so, disable DWIM mode
    ++		local track_opt="--track" only_local_ref=n
    ++		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
    ++		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
    ++			track_opt=''
    ++		fi
    ++		# explicit --guess enables DWIM mode regardless of
    ++		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
    ++		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
     +			track_opt='--track'
     +		fi
     +		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
     +			only_local_ref=y
     +		fi
    -+		if [ $only_local_ref = y -a -n "$track_opt"]; then
    ++		if [ $only_local_ref = y -a -z "$track_opt" ]; then
     +			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
     +		else
     +			__git_complete_refs $track_opt
19:  8ed01ad0c5 ! 24:  807e8bc50b doc: promote "git switch"
    @@ -647,23 +647,28 @@
      void detach_advice(const char *new_name)
      {
      	const char *fmt =
    --	_("Note: checking out '%s'.\n\n"
    +-	_("Note: checking out '%s'.\n"
     +	_("Note: switching to '%s'.\n"
    -+	"\n"
    + 	"\n"
      	"You are in 'detached HEAD' state. You can look around, make experimental\n"
      	"changes and commit them, and you can discard any commits you make in this\n"
    --	"state without impacting any branches by performing another checkout.\n\n"
    +-	"state without impacting any branches by performing another checkout.\n"
     +	"state without impacting any branches by switching back to a branch.\n"
    -+	"\n"
    + 	"\n"
      	"If you want to create a new branch to retain commits you create, you may\n"
    --	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
    --	"  git checkout -b <new-branch-name>\n\n");
    +-	"do so (now or later) by using -b with the checkout command again. Example:\n"
     +	"do so (now or later) by using -c with the switch command. Example:\n"
    -+	"\n"
    -+	"  git switch -c <new-branch-name>\n\n");
    + 	"\n"
    +-	"  git checkout -b <new-branch-name>\n"
    ++	"  git switch -c <new-branch-name>\n"
    + 	"\n"
    + 	"Or undo this checkout with:\n"
    + 	"\n"
    +-	"  git checkout -\n"
    ++	"  git switch -\n"
    + 	"\n"
    + 	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
      
    - 	fprintf(stderr, fmt, new_name);
    - }
     
      diff --git a/sha1-name.c b/sha1-name.c
      --- a/sha1-name.c
    @@ -700,8 +705,13 @@
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
      
    - 	HEAD is now at \$commit three
    - 	EOF
    + 	Or undo this checkout with:
    + 
    +-	  git checkout -
    ++	  git switch -
    + 
    + 	Turn off this advice by setting config variable advice.detachedHead to false
    + 
     @@
      
      	# The first detach operation is more chatty than the following ones.
    @@ -721,5 +731,10 @@
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
      
    - 	HEAD is now at \$commit... three
    - 	EOF
    + 	Or undo this checkout with:
    + 
    +-	  git checkout -
    ++	  git switch -
    + 
    + 	Turn off this advice by setting config variable advice.detachedHead to false
    + 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v4 01/26] git-checkout.txt: spell out --no-option
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                         ` (26 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

It's easier to search for and also less cryptic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index f179b43732..99c8c0dc0f 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,7 +118,8 @@ OPTIONS
 --quiet::
 	Quiet, suppress feedback messages.
 
---[no-]progress::
+--progress::
+--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
@@ -262,7 +263,7 @@ 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.
+`--overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
---[no-]recurse-submodules::
+--recurse-submodules::
+--no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also
 	Do not attempt to create a branch if a remote tracking branch
 	of the same name exists.
 
---[no-]overlay::
+--overlay::
+--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
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 02/26] git-checkout.txt: fix one syntax line
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                         ` (25 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 99c8c0dc0f..28817cfa41 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 03/26] doc: document --overwrite-ignore
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 19:18         ` Martin Ågren
  2019-03-17 12:49       ` [PATCH v4 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
                         ` (24 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 14 ++++++++++----
 Documentation/git-merge.txt    |  5 +++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 28817cfa41..82e0f81869 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -271,15 +271,21 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort
+	the operation when the new branch contains ignored files.
+
 --recurse-submodules::
 --no-recurse-submodules::
-	Using --recurse-submodules will update the content of all initialized
+	Using `--recurse-submodules` will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
-	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
 
 --no-guess::
 	Do not attempt to create a branch if a remote tracking branch
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..6a9163d8fe 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,11 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 04/26] git-checkout.txt: fix monospace typeset
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (2 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 19:21         ` Martin Ågren
  2019-03-17 12:49       ` [PATCH v4 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                         ` (23 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Add backticks where we have none, replace single quotes with backticks
and replace double-quotes. Drop double-quotes from nested constructions
such as `"@{-1}"`. Add a missing possessive apostrophe after the word
"submodules" while at it.

Helped-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 154 ++++++++++++++++-----------------
 1 file changed, 77 insertions(+), 77 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 82e0f81869..bf90966c95 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-	To prepare for working on <branch>, switch to it by updating
+	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
-	HEAD at the branch. Local modifications to the files in the
+	`HEAD` at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
-	<branch>.
+	`<branch>`.
 +
-If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
+If `<branch>` is not found but there does exist a tracking branch in
+exactly one remote (call it `<remote>`) with a matching name, treat as
 equivalent to
 +
 ------------
@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the
 'origin' remote. See also `checkout.defaultRemote` in
 linkgit:git-config[1].
 +
-You could omit <branch>, in which case the command degenerates to
+You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
@@ -61,7 +61,7 @@ if exists, for the current branch.
 	`--track` without `-b` implies branch creation; see the
 	description of `--track` below.
 +
-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
 is reset. This is the transactional equivalent of
 +
 ------------
@@ -75,25 +75,25 @@ successful.
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of <commit>, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching `HEAD` at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
 	tree will be the state recorded in the commit plus the local
 	modifications.
 +
-When the <commit> argument is a branch name, the `--detach` option can
-be used to detach HEAD at the tip of the branch (`git checkout
-<branch>` would check out that branch without detaching HEAD).
+When the `<commit>` argument is a branch name, the `--detach` option can
+be used to detach `HEAD` at the tip of the branch (`git checkout
+<branch>` would check out that branch without detaching `HEAD`).
 +
-Omitting <branch> detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
 	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the <tree-ish> (most often a
-	commit).  When a <tree-ish> is given, the paths that
-	match the <pathspec> are updated both in the index and in
+	contents in the index or in the `<tree-ish>` (most often a
+	commit).  When a `<tree-ish>` is given, the paths that
+	match the `<pathspec>` are updated both in the index and in
 	the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
@@ -128,7 +128,7 @@ OPTIONS
 -f::
 --force::
 	When switching branches, proceed even if the index or the
-	working tree differs from HEAD.  This is used to throw away
+	working tree differs from `HEAD`.  This is used to throw away
 	local changes.
 +
 When checking out paths from the index, do not fail upon unmerged
@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new_branch>::
-	Create a new branch named <new_branch> and start it at
-	<start_point>; see linkgit:git-branch[1] for details.
+	Create a new branch named `<new_branch>` and start it at
+	`<start_point>`; see linkgit:git-branch[1] for details.
 
 -B <new_branch>::
-	Creates the branch <new_branch> and start it at <start_point>;
-	if it already exists, then reset it to <start_point>. This is
+	Creates the branch `<new_branch>` and start it at `<start_point>`;
+	if it already exists, then reset it to `<start_point>`. This is
 	equivalent to running "git branch" with "-f"; see
 	linkgit:git-branch[1] for details.
 
@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
 the initial part up to the "*".
-This would tell us to use "hack" as the local branch when branching
-off of "origin/hack" (or "remotes/origin/hack", or even
-"refs/remotes/origin/hack").  If the given name has no slash, or the above
+This would tell us to use `hack` as the local branch when branching
+off of `origin/hack` (or `remotes/origin/hack`, or even
+`refs/remotes/origin/hack`).  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with `-b` in such a case.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
-	branch.autoSetupMerge configuration variable is true.
+	`branch.autoSetupMerge` configuration variable is true.
 
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.
 --detach::
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
-	This is the default behavior of "git checkout <commit>" when
-	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	This is the default behavior of `git checkout <commit>` when
+	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
 --orphan <new_branch>::
-	Create a new 'orphan' branch, named <new_branch>, started from
-	<start_point> and switch to it.  The first commit made on this
+	Create a new 'orphan' branch, named `<new_branch>`, started from
+	`<start_point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
-"git checkout <start_point>".  This allows you to start a new history
-that records a set of paths similar to <start_point> by easily running
-"git commit -a" to make the root commit.
+`git checkout <start_point>`.  This allows you to start a new history
+that records a set of paths similar to `<start_point>` by easily running
+`git commit -a` to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
 without exposing its full history. You might want to do this to publish
@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of
 code.
 +
 If you want to start a disconnected history that records a set of paths
-that is totally different from the one of <start_point>, then you should
+that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
-branch by running "git rm -rf ." from the top level of the working tree.
+branch by running `git rm -rf .` from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
-	update only entries matched by <paths> and sparse patterns
-	in $GIT_DIR/info/sparse-checkout. This option ignores
-	the sparse patterns and adds back any files in <paths>.
+	update only entries matched by `<paths>` and sparse patterns
+	in `$GIT_DIR/info/sparse-checkout`. This option ignores
+	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
 --merge::
@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
 
 --conflict=<style>::
-	The same as --merge option above, but changes the way the
+	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.conflictStyle` configuration variable.  Possible values are
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	<tree-ish> (or the index, if unspecified) and the working
+	`<tree-ish>` (or the index, if unspecified) and the working
 	tree.  The chosen hunks are then applied in reverse to the
-	working tree (and if a <tree-ish> was specified, the index).
+	working tree (and if a `<tree-ish>` was specified, the index).
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also
 	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.
+	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
 	branch is checked out. Otherwise, if it refers to a valid
-	commit, your HEAD becomes "detached" and you are no longer on
+	commit, your `HEAD` becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-You can use the `"@{-N}"` syntax to refer to the N-th last
+You can use the `@{-N}` syntax to refer to the N-th last
 branch/commit checked out using "git checkout" operation. You may
-also specify `-` which is synonymous to `"@{-1}"`.
+also specify `-` which is synonymous to `@{-1}`.
 +
-As a special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `A...B` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <start_point>::
 	The name of a commit at which to start the new branch; see
-	linkgit:git-branch[1] for details. Defaults to HEAD.
+	linkgit:git-branch[1] for details. Defaults to `HEAD`.
 
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 DETACHED HEAD
 -------------
-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
 branch refers to a specific commit. Let's look at a repo with three
-commits, one of them tagged, and with branch 'master' checked out:
+commits, one of them tagged, and with branch `master` checked out:
 
 ------------
            HEAD (refers to branch 'master')
@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')
 ------------
 
 When a commit is created in this state, the branch is updated to refer to
-the new commit. Specifically, 'git commit' creates a new commit 'd', whose
-parent is commit 'c', and then updates branch 'master' to refer to new
-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
-to commit 'd':
+the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+parent is commit `c`, and then updates branch `master` to refer to new
+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+to commit `d`:
 
 ------------
 $ edit; git add; git commit
@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 It is sometimes useful to be able to checkout a commit that is not at
 the tip of any named branch, or even to create a new commit that is not
 referenced by a named branch. Let's look at what happens when we
-checkout commit 'b' (here we show two ways this may be done):
+checkout commit `b` (here we show two ways this may be done):
 
 ------------
 $ git checkout v2.0  # or
@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-Notice that regardless of which checkout command we use, HEAD now refers
-directly to commit 'b'. This is known as being in detached HEAD state.
-It means simply that HEAD refers to a specific commit, as opposed to
+Notice that regardless of which checkout command we use, `HEAD` now refers
+directly to commit `b`. This is known as being in detached `HEAD` state.
+It means simply that `HEAD` refers to a specific commit, as opposed to
 referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-There is now a new commit 'e', but it is referenced only by HEAD. We can
+There is now a new commit `e`, but it is referenced only by `HEAD`. We can
 of course add yet another commit in this state:
 
 ------------
@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 In fact, we can perform all the normal Git operations. But, let's look
-at what happens when we then checkout master:
+at what happens when we then checkout `master`:
 
 ------------
 $ git checkout master
@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 It is important to realize that at this point nothing refers to commit
-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
 by the routine Git garbage collection process, unless we create a reference
-before that happens. If we have not yet moved away from commit 'f',
+before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
@@ -442,19 +442,19 @@ $ git branch foo        <2>
 $ git tag foo           <3>
 ------------
 
-<1> creates a new branch 'foo', which refers to commit 'f', and then
-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-    be in detached HEAD state after this command.
+<1> creates a new branch `foo`, which refers to commit `f`, and then
+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+    be in detached `HEAD` state after this command.
 
-<2> similarly creates a new branch 'foo', which refers to commit 'f',
-    but leaves HEAD detached.
+<2> similarly creates a new branch `foo`, which refers to commit `f`,
+    but leaves `HEAD` detached.
 
-<3> creates a new tag 'foo', which refers to commit 'f',
-    leaving HEAD detached.
+<3> creates a new tag `foo`, which refers to commit `f`,
+    leaving `HEAD` detached.
 
-If we have moved away from commit 'f', then we must first recover its object
+If we have moved away from commit `f`, then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
-it. For example, to see the last two commits to which HEAD referred, we
+it. For example, to see the last two commits to which `HEAD` referred, we
 can use either of these commands:
 
 ------------
@@ -465,12 +465,12 @@ $ git log -g -2 HEAD
 ARGUMENT DISAMBIGUATION
 -----------------------
 
-When there is only one argument given and it is not `--` (e.g. "git
-checkout abc"), and when the argument is both a valid `<tree-ish>`
-(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+When there is only one argument given and it is not `--` (e.g. `git
+checkout abc`), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
 or a directory whose name is "abc" exists), Git would usually ask
 you to disambiguate.  Because checking out a branch is so common an
-operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
 in such a situation.  Use `git checkout -- <pathspec>` if you want
 to checkout these paths out of the index.
 
@@ -478,7 +478,7 @@ EXAMPLES
 --------
 
 . The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes hello.c by
+  the `Makefile` to two revisions back, deletes `hello.c` by
   mistake, and gets it back from the index.
 +
 ------------
@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>
 +
 <1> switch branch
 <2> take a file out of another commit
-<3> restore hello.c from the index
+<3> restore `hello.c` from the index
 +
 If you want to check out _all_ C source files out of the index,
 you can say
@@ -519,7 +519,7 @@ $ git checkout -- hello.c
 $ git checkout mytopic
 ------------
 +
-However, your "wrong" branch and correct "mytopic" branch may
+However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (3 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
                         ` (22 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v4 06/26] checkout: advice how to get out of detached HEAD mode
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (4 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
                         ` (21 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Detached HEAD mode is considered dangerous and confusing for newcomers
and we print a big block of warning how to move forward. But we should
also suggest the user the way to get out of it if they get into detached
HEAD by mistake.

While at there, I also suggest how to turn the advice off. This is
another thing I find annoying with advices and should be dealt with in a
more generic way. But that may require some refactoring in advice.c
first.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c                   | 17 +++++++++++++----
 t/t2020-checkout-detach.sh | 12 ++++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/advice.c b/advice.c
index 567209aa79..b224825637 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: checking out '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by performing another checkout.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"\n"
+	"  git checkout -b <new-branch-name>\n"
+	"\n"
+	"Or undo this checkout with:\n"
+	"\n"
+	"  git checkout -\n"
+	"\n"
+	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..743c7248a2 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit three
 	EOF
 
@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit... three
 	EOF
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 07/26] checkout: keep most #include sorted
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (5 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                         ` (20 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

The include list becomes very long and frankly a bit unorganized. With
the exception of builtin.h, cache.h or git-compat-util.h which have to
come first, keep the rest sorted.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0e6037b296..f7801db2fb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,30 +1,30 @@
 #define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
-#include "config.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
 #include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
 #include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
 #include "parse-options.h"
 #include "refs.h"
-#include "object-store.h"
-#include "commit.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "resolve-undo.h"
-#include "submodule-config.h"
-#include "submodule.h"
-#include "advice.h"
 
 static int checkout_optimize_new_branch;
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 08/26] checkout: factor out some code in parse_branchname_arg()
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (6 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                         ` (19 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f7801db2fb..1ce0446bc6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 09/26] checkout: make "opts" in cmd_checkout() a pointer
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (7 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                         ` (18 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1ce0446bc6..963f10b05b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (8 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                         ` (17 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 963f10b05b..b6fc1fc69f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -47,6 +47,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -58,6 +60,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 11/26] checkout: split options[] array in three pieces
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (9 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (16 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  1 +
 3 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index b6fc1fc69f..cf1d70511e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 2733393546..caaeed896f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 7d83e2971d..9a90c332a5 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+struct option *parse_options_dup(const struct option *a);
 struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 12/26] checkout: split part of it to new command 'switch'
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (10 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-18 16:48         ` Elijah Newren
  2019-03-17 12:49       ` [PATCH v4 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                         ` (15 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and restore. The good
old "git checkout" command is still here and will be until all (or most
of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                         |   1 +
 Documentation/config/advice.txt    |  13 +-
 Documentation/config/branch.txt    |   4 +-
 Documentation/config/checkout.txt  |   9 +-
 Documentation/config/diff.txt      |   3 +-
 Documentation/git-checkout.txt     |   4 +
 Documentation/git-switch.txt (new) | 277 +++++++++++++++++++++++++++++
 Documentation/gitattributes.txt    |   3 +-
 Documentation/githooks.txt         |   8 +-
 Makefile                           |   1 +
 builtin.h                          |   1 +
 builtin/checkout.c                 |  60 +++++--
 command-list.txt                   |   1 +
 git.c                              |   1 +
 14 files changed, 359 insertions(+), 27 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7374587f9d..c687b92b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index c4118fa196..d6872ffa83 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index bf90966c95..c7192bdefe 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -560,6 +560,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..74415f12ab
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,277 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c`, `-C`,
+automatically from a remote branch of same name (see `--guess`), or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to `HEAD`). The operation is aborted
+however if the switch leads to loss of local changes, unless told
+otherwise.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The starting point for the new branch. Specifying a
+	`<start-point>` allows you to create a branch based on some
+	other point in history than where HEAD currently points. (Or,
+	in the case of `--detach`, allows you to inspect and detach
+	from some other point.)
++
+You can use the `@{-N}` syntax to refer to the N-th last
+branch/commit switched to "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `@{-1}`.
+This is often used to switch quickly between two branches, or to undo
+a branch switch by mistake.
++
+As a special case, you may use `A...B` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named `<new-branch>` starting at
+	`<start-point>` before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if `<new-branch>` already
+	exists, it will be reset to `<start-point>`. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
++
+`--guess` is the default behavior. Use `--no-guess` to disable it.
+
+-f::
+--force::
+	An alias for `--discard-changes`.
+
+--discard-changes::
+	Proceed even if the index or the working tree differs from
+	`HEAD`. Both the index and working tree are restored to match
+	the switching target. This is used to throw away local
+	changes.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+--progress::
+--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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See `--track` in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use `hack` as
+the local branch when branching off of `origin/hack` (or
+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	`branch.autoSetupMerge` configuration variable is true.
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named `<new-branch>`. If
+	`<start-point>` is specified, the index and working tree are
+	adjusted to match it. Otherwise both are adjusted to contain no
+	tracked files.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--ignore-in-progress::
+	`git switch` by default refuses when some operation is in
+	progress (e.g. "git rebase", "git am" ...). This option
+	overrides this safety check and allows switching.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name:
+
+------------
+$ git switch -g new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit `HEAD~3` for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-branch[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 9b41f81c06..cd0f9fa507 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 5bf653c111..8ff72f0613 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -406,7 +407,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 148668368b..8e91db73ad 100644
--- a/Makefile
+++ b/Makefile
@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cf1d70511e..dd3c200cb5 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 2014aab6b8..39582cf511 100644
--- a/git.c
+++ b/git.c
@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 13/26] switch: better names for -b and -B
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (11 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
                         ` (14 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout. Move -b/-B
to cmd_checkout() and new -c/-C with the same functionality in
cmd_switch_branch()

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index dd3c200cb5..400a0d5620 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 14/26] switch: add --discard-changes
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (12 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-18 23:55         ` Eric Sunshine
  2019-03-17 12:49       ` [PATCH v4 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
                         ` (13 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

--discard-changes is a better name than --force for this option since
it's what really happens. --force is turned to an alias for
--discard-changes. But it's meant to an alias for potentially more force
options in the future.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 400a0d5620..bd94b04303 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int discard_changes;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
-	if (opts->force) {
+	if (opts->discard_changes) {
 		ret = reset_tree(get_commit_tree(new_branch_info->commit),
 				 opts, 1, writeout_error);
 		if (ret)
@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->force && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->force && opts->merge)
 		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
 
+	if (opts->discard_changes && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
 	if (opts->force_detach && opts->new_branch)
 		die(_("'%s' cannot be used with '%s'"),
 		    "--detach", "-b/-B/--orphan");
@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
+	if (opts->force)
+		opts->discard_changes = 1;
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+			 N_("throw away local modifications")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 15/26] switch: remove -l
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (13 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                         ` (12 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index bd94b04303..85fc60f5f2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 16/26] switch: stop accepting pathspec
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (14 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                         ` (11 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 85fc60f5f2..2c4cab6406 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -54,6 +54,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int discard_changes;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 17/26] switch: reject "do nothing" case
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (15 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                         ` (10 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2c4cab6406..73b7373414 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -55,6 +55,7 @@ struct checkout_opts {
 	int no_dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 18/26] switch: only allow explicit detached HEAD
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (16 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                         ` (9 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 73b7373414..d72e28aabb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -45,6 +45,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
 	return status;
 }
 
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+	struct object_id oid;
+	char *to_free;
+
+	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+		const char *ref = to_free;
+
+		if (skip_prefix(ref, "refs/tags/", &ref))
+			die(_("a branch is expected, got tag '%s'"), ref);
+		if (skip_prefix(ref, "refs/remotes/", &ref))
+			die(_("a branch is expected, got remote branch '%s'"), ref);
+		die(_("a branch is expected, got '%s'"), ref);
+	}
+	if (branch_info->commit)
+		die(_("a branch is expected, got commit '%s'"), branch_info->name);
+	/*
+	 * This case should never happen because we already die() on
+	 * non-commit, but just in case.
+	 */
+	die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("missing branch or commit argument"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die_expecting_a_branch(new_branch_info);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 19/26] switch: add short option for --detach
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (17 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
                         ` (8 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index d72e28aabb..a14133b474 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 20/26] switch: implicit dwim, use --no-guess to disable it
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (18 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                         ` (7 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

This is already the default in git-checkout. The real change in here is
just minor cleanup. The main excuse is to explain why dwim is kept default.

Contrary to detach mode that is easy to get into and confusing to get
back out. Automatically creating a tracking branch often does not kick
in as often (you would need a branch of the same name on a remote). And
since the branch creation is reported clearly, the user should be able
to undo/delete it if it's unwanted.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++-------
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index c7192bdefe..58f18a0842 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	`<branch>`.
 +
 If `<branch>` is not found but there does exist a tracking branch in
-exactly one remote (call it `<remote>`) with a matching name, treat as
-equivalent to
+exactly one remote (call it `<remote>`) with a matching name and
+`--no-guess` is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	`branch.autoSetupMerge` configuration variable is true.
 
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
 	submodule.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --overlay::
 --no-overlay::
 	In the default overlay mode, `git checkout` never
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a14133b474..ee74379038 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,7 +53,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
 		OPT_END()
@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 21/26] switch: no worktree status unless real branch switch happens
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (19 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
                         ` (6 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 134 ++----------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 8 insertions(+), 148 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index d6872ffa83..6b646813ab 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
-
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ee74379038..2aeaac092d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -26,8 +26,6 @@
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -57,11 +55,7 @@ struct checkout_opts {
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	 /*
-	  * Do the merge if this is the initial checkout. We cannot use
-	  * is_cache_unborn() here because the index hasn't been loaded yet
-	  * so cache_nr and timestamp.sec are always zero.
-	  */
-	if (!file_exists(get_index_file()))
-		return 0;
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
 
 	trace2_cmd_mode("branch");
 
@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "diff.ignoresubmodules")) {
 		struct checkout_opts *opts = cb;
 		handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 22/26] switch: reject if some operation is in progress
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (20 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-20 11:16         ` Phillip Wood
  2019-03-17 12:49       ` [PATCH v4 23/26] switch: --orphan defaults to empty tree as HEAD Nguyễn Thái Ngọc Duy
                         ` (5 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Unless you know what you're doing, switching to another branch to do
something then switching back could be confusing. Worse, you may even
forget that you're in the middle of something. By the time you realize,
you may have done a ton of work and it gets harder to go back.

The new option --ignore-in-progress is added to override this check.
However it's probably just safer to create a separate worktree for
that separate thing you want to work on and leave this worktree
alone (unless of course creating or preparing worktrees are not cheap).

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2aeaac092d..21a585eaa9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -24,6 +24,7 @@
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
+#include "wt-status.h"
 #include "xdiff-interface.h"
 
 static const char * const checkout_usage[] = {
@@ -56,6 +57,7 @@ struct checkout_opts {
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
+	int can_switch_when_in_progress;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !new_branch_info->path)
 		die_expecting_a_branch(new_branch_info);
 
+	if (!opts->can_switch_when_in_progress) {
+		struct wt_status_state state;
+
+		memset(&state, 0, sizeof(state));
+		wt_status_get_state(the_repository, &state, 0);
+
+		if (state.merge_in_progress)
+			die(_("cannot switch branch while merging"));
+		if (state.am_in_progress)
+			die(_("cannot switch branch in the middle of an am session"));
+		if (state.rebase_in_progress || state.rebase_in_progress)
+			die(_("cannot switch branch while rebasing"));
+		if (state.cherry_pick_in_progress)
+			die(_("cannot switch branch while cherry-picking"));
+		if (state.revert_in_progress)
+			die(_("cannot switch branch while reverting"));
+		if (state.bisect_in_progress)
+			die(_("cannot switch branch while bisecting"));
+	}
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1514,6 +1536,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
+	opts.can_switch_when_in_progress = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1539,6 +1562,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
+		OPT_BOOL(0, "ignore-in-progress", &opts.can_switch_when_in_progress,
+			 N_("allow to switch branch when some operation is still in progress")),
 		OPT_END()
 	};
 	int ret;
@@ -1549,6 +1574,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
+	opts.can_switch_when_in_progress = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 23/26] switch: --orphan defaults to empty tree as HEAD
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (21 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
                         ` (4 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Switching and creating branches always involves knowing the
<start-point> to begin the new branch from. Sometimes, people want to
create a new branch that does not have any commits yet; --orphan is a
flag to allow that.

If <start-point> is not specified, instead of leaving index/worktree
unchanged, default to "empty tree" starting point, allowing a clean
start.

A note on the implementation. An alternative is just create a dummy
commit in-core with empty tree and switch to it. But there's a chance
the commit's SHA-1 may end up somewhere permanent like reflog. It's best
to make sure "commit" pointer is NULL to avoid it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 21a585eaa9..f0d6207236 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -58,6 +58,7 @@ struct checkout_opts {
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
 	int can_switch_when_in_progress;
+	int orphan_default_empty_tree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -568,15 +569,19 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
 	int ret;
 	struct lock_file lock_file = LOCK_INIT;
+	struct tree *new_tree;
 
 	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(NULL) < 0)
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
+	if (opts->orphan_default_empty_tree && !new_branch_info->commit)
+		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+	else
+		new_tree = get_commit_tree(new_branch_info->commit);
 	if (opts->discard_changes) {
-		ret = reset_tree(get_commit_tree(new_branch_info->commit),
-				 opts, 1, writeout_error);
+		ret = reset_tree(new_tree, opts, 1, writeout_error);
 		if (ret)
 			return ret;
 	} else {
@@ -614,7 +619,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 					   &old_branch_info->commit->object.oid :
 					   the_hash_algo->empty_tree);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+		parse_tree(new_tree);
+		tree = new_tree;
 		init_tree_desc(&trees[1], tree->buffer, tree->size);
 
 		ret = unpack_trees(2, trees, &topts);
@@ -663,7 +669,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.verbosity = 0;
 			work = write_tree_from_memory(&o);
 
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 1,
 					 writeout_error);
 			if (ret)
@@ -672,13 +678,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.branch1 = new_branch_info->name;
 			o.branch2 = "local";
 			ret = merge_trees(&o,
-					  get_commit_tree(new_branch_info->commit),
+					  new_tree,
 					  work,
 					  get_commit_tree(old_branch_info->commit),
 					  &result);
 			if (ret < 0)
 				exit(128);
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 0,
 					 writeout_error);
 			strbuf_release(&o.obuf);
@@ -696,7 +702,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->discard_changes && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -897,7 +903,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
 	for_each_ref(add_pending_uninteresting_ref, &revs);
-	add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
 
 	if (prepare_revision_walk(&revs))
 		die(_("internal error in revision walk"));
@@ -941,6 +950,12 @@ static int switch_branches(const struct checkout_opts *opts,
 
 		if (opts->only_merge_on_switching_branches)
 			do_merge = 0;
+
+		if (opts->new_orphan_branch && opts->orphan_default_empty_tree) {
+			new_branch_info->commit = NULL;
+			new_branch_info->name = "(empty)";
+			do_merge = 1;
+		}
 	}
 
 	if (do_merge) {
@@ -1537,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
+	opts.orphan_default_empty_tree = 0;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1575,6 +1591,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 	opts.can_switch_when_in_progress = 0;
+	opts.orphan_default_empty_tree = 1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 24/26] t: add tests for switch
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (22 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 23/26] switch: --orphan defaults to empty tree as HEAD Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 25/26] completion: support switch Nguyễn Thái Ngọc Duy
                         ` (3 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2060-switch.sh (new +x) | 97 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..62ec1bb924
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	test_must_fail git switch master^{commit} &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	test_must_fail git switch -c temp &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch from empty' '
+	test_when_finished git switch master &&
+	git switch --orphan new-orphan &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit &&
+	git ls-files >tracked-files &&
+	echo orphan.t >expected &&
+	test_cmp expected tracked-files
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --no-guess foo &&
+	git switch foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'switching when something is in progress' '
+	test_when_finished rm -f .git/MERGE_HEAD &&
+	# fake a merge-in-progress
+	cp .git/HEAD .git/MERGE_HEAD &&
+	test_must_fail git switch -d @^ &&
+	git switch --ignore-in-progress -d @^ &&
+	git switch -
+'
+
+test_done
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 25/26] completion: support switch
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (23 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:49       ` [PATCH v4 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
                         ` (2 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 976e4a6548..b24bc48276 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -37,7 +37,8 @@
 #   GIT_COMPLETION_CHECKOUT_NO_GUESS
 #
 #     When set to "1", do not include "DWIM" suggestions in git-checkout
-#     completion (e.g., completing "foo" when "origin/foo" exists).
+#     and git-switch completion (e.g., completing "foo" when "origin/foo"
+#     exists).
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -2158,6 +2159,40 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local track_opt="--track" only_local_ref=n
+		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
+			track_opt=''
+		fi
+		# explicit --guess enables DWIM mode regardless of
+		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
+		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
+			track_opt='--track'
+		fi
+		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y -a -z "$track_opt" ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v4 26/26] doc: promote "git switch"
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (24 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 25/26] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:49       ` Nguyễn Thái Ngọc Duy
  2019-03-17 12:58       ` [PATCH v4 00/26] Add new command 'switch' Duy Nguyen
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-17 12:49 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller,
	phillip.wood123, rybak.a.v

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-branch.txt           | 12 +++---
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 ++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 19 +++++----
 Documentation/giteveryday.txt          | 24 +++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/gitworkflows.txt         |  3 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 56 +++++++++++++-------------
 advice.c                               | 10 ++---
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             | 20 ++++-----
 18 files changed, 101 insertions(+), 101 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0cd87ddeff..1e2d89b174 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -297,7 +297,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 1af85d404f..0a24a5679e 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5629ba4c5d..cb6fc166e2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 132f8e55f6..cbf901efb4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..f880d21dfb 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
+$ git switch -C mybranch master^2
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..ad455f3e39 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch -C pu next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index ca11c7bdaf..abc0dc6bc7 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -301,8 +301,7 @@ topics on 'next':
 .Rewind and rebuild next
 [caption="Recipe: "]
 =====================================
-* `git checkout next`
-* `git reset --hard master`
+* `git switch -C next master`
 * `git merge ai/topic_in_next1`
 * `git merge ai/topic_in_next2`
 * ...
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..94799faa2b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index b224825637..27e39e6514 100644
--- a/advice.c
+++ b/advice.c
@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n"
+	_("Note: switching to '%s'.\n"
 	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n"
+	"state without impacting any branches by switching back to a branch.\n"
 	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"do so (now or later) by using -c with the switch command. Example:\n"
 	"\n"
-	"  git checkout -b <new-branch-name>\n"
+	"  git switch -c <new-branch-name>\n"
 	"\n"
 	"Or undo this checkout with:\n"
 	"\n"
-	"  git checkout -\n"
+	"  git switch -\n"
 	"\n"
 	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
diff --git a/sha1-name.c b/sha1-name.c
index 6dda2c16df..da0518c8e3 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 743c7248a2..da6809b48f 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	Or undo this checkout with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	Or undo this checkout with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 00/26] Add new command 'switch'
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (25 preceding siblings ...)
  2019-03-17 12:49       ` [PATCH v4 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-17 12:58       ` Duy Nguyen
  2019-03-18  4:54         ` Junio C Hamano
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  27 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-17 12:58 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Elijah Newren, Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

On Sun, Mar 17, 2019 at 7:49 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Thanks for all the comments from v3 (and before), I didn't expect
> feedback from so many people. v4 fixes most of them, but still leaves
> a couple for v5.
>
> - -C remains because people seem to need it
>
> - --recreate vs --force-create: there was no strong preference for
>   either, so I stick with --force-create
>
> - --guess (dwim mode) is now made default
>
> - --orphan by default switches to an empty tree instead of current HEAD
>   (this is Elijah's alternative approach). This allows you to start
>   fresh, but you can still keep "HEAD" content or even switch to another
>   tree.
>
> - advice.detachedHead text is updated to suggest a way out (currently it
>   only suggests a way forward with 'checkout -b')
>
> - --force has a new alias --discard-changes
>
> - the error message when we expect a branch is improved to help point
>   out why the given argument is not a branch
>
> - more docs update and reorganization, and other silly bugs I made in
>   v3

I did forget one thing:

- 'git switch' refuses to switch in the middle of
rebase/bisect/cherry-pick/... session. Add --ignore-in-progress to
override this

Now that I mention this, --ignore-in-progress should probably be part
of --force too. Hmm...
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 03/26] doc: document --overwrite-ignore
  2019-03-17 12:49       ` [PATCH v4 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-03-17 19:18         ` Martin Ågren
  0 siblings, 0 replies; 289+ messages in thread
From: Martin Ågren @ 2019-03-17 19:18 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Elijah Newren, eckhard.s.maass, Jacob Keller,
	phillip.wood123, rybak.a.v

On Sun, 17 Mar 2019 at 13:49, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> I added this option in git-checkout and git-merge in c1d7036b6b
> (checkout,merge: disallow overwriting ignored files with
> --no-overwrite-ignore - 2011-11-27) but did not remember to update
> documentation. This completes that commit.

> +--overwrite-ignore::
> +--no-overwrite-ignore::
> +       Silently overwrite ignored files when switching branches. This
> +       is the default behavior. Use `--no-overwrite-ignore` to abort
> +       the operation when the new branch contains ignored files.
> +
>  --recurse-submodules::
>  --no-recurse-submodules::
> -       Using --recurse-submodules will update the content of all initialized
> +       Using `--recurse-submodules` will update the content of all initialized
>         submodules according to the commit recorded in the superproject. If
>         local modifications in a submodule would be overwritten the checkout
> -       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
> +       will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
>         is used, the work trees of submodules will not be updated.
> -       Just like linkgit:git-submodule[1], this will detach the
> -       submodules HEAD.
> +       Just like linkgit:git-submodule[1], this will detach `HEAD` of the
> +       submodule.

All of this `--recurse-submodules` stuff seems to have have ended
up in the wrong commit.


Martin

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 04/26] git-checkout.txt: fix monospace typeset
  2019-03-17 12:49       ` [PATCH v4 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-17 19:21         ` Martin Ågren
  0 siblings, 0 replies; 289+ messages in thread
From: Martin Ågren @ 2019-03-17 19:21 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Elijah Newren, eckhard.s.maass, Jacob Keller,
	phillip.wood123, rybak.a.v

On Sun, 17 Mar 2019 at 13:50, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Add backticks where we have none, replace single quotes with backticks
> and replace double-quotes. Drop double-quotes from nested constructions
> such as `"@{-1}"`. Add a missing possessive apostrophe after the word
> "submodules" while at it.

Assuming you move those "submodules" hunks from an earlier commit, this
last part needs some tweaking since you're not adding a "'", but you
actually rephrase the docs to avoid a buggy AsciiDoc rendering. (And if
a while-at-it needs too much explaining, maybe just skip it altogether?
I dunno.)

Other than some things appearing in the wrong commit(?), this matches
the "how about this?" I sent for v3 and our discussion of that.



Martin

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-14 14:46                         ` Elijah Newren
@ 2019-03-18  2:03                           ` Junio C Hamano
  2019-03-18 12:06                             ` Duy Nguyen
  2019-03-18 19:10                             ` Elijah Newren
  0 siblings, 2 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-18  2:03 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Duy Nguyen, Eric Sunshine, Phillip Wood, Git Mailing List,
	SZEDER Gábor, Martin Ågren

Elijah Newren <newren@gmail.com> writes:

> I don't see why <start-point> even makes sense to use with --orphan;
> you should error if both are given, IMO.  The point of --orphan is to
> create some entirely new history.  So, I'd expect "git switch --orphan
> <new-branch>" to:
>   * not create refs/heads/<new-branch>
>   * set HEAD to refs/heads/<new-branch>
>   * empty all tracked files from the working tree.
>   * empty the index
>
> Alternatively, you could allow <start-point> to be passed with
> --orphan, adjusting the above steps so that both the index and the
> working tree are switched to match <start-point>, but ONLY if
> <start-point> defaults to the empty tree when --orphan is passed.

Do you mean that it's like <start-point> is not really a start-point
but is an initial tree, i.e.

	switch --orphan --initial-tree=<tree-ish> <new-branch>

is a mere short-hand for

	switch --orhpan <new-branch> &&
	restore --from-tree=<tree-ish> .

I think that does make sense, but at the same time, I think a major
reason why people say "checkout does too many things depending on
the arguments and conext to be easily explained" is exactly due to
its many "if you give X, it is like writing this longer command
sequence" short hands, so...

> How about:
>
> """
> Switching and creating branches always involves knowing the
> <start-point> to begin the new branch from.  Sometimes, people want to
> create a new branch that does not have any commits yet; --orphan is a
> flag to allow that.  --orphan overrides the default of HEAD for
> <start-point> instead causing us to start from an empty history.  The
> use of --orphan is incompatible with specifying a <start-point>.
> """

With or without the short-hand to say which initial tree to populate
the index from, the above description makes sense to me.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-17  6:00         ` [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index Nguyễn Thái Ngọc Duy
@ 2019-03-18  3:58           ` Junio C Hamano
  2019-03-18  9:24             ` Duy Nguyen
  2019-03-18 11:38           ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  1 sibling, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-18  3:58 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: phillip.wood123, git, martin.agren, sunshine, szeder.dev, Elijah Newren

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> One-way merge is supposed to take stat info from the index and
> everything else from the given tree. This implies stage 0 because trees
> can't have non-zero stages. The add_entry(.., old, ...) call however
> will keep stage index from the index.
>
> This is normally not a problem if the entry from the index is
> normal (stage #0). But if there is a conflict, we'll get stage #1 entry
> as "old" and it gets recorded in the final index. Fix it by clearing
> stage mask.
>
> This bug probably comes from b5b425074e (git-read-tree: make one-way
> merge also honor the "update" flag, 2005-06-07). Before this commit, we
> may create the final ("dst") index entry from the one in index, but we
> do clear CE_STAGEMASK.

Wow, good find.  That's an old one.

> I briefly checked two- and three-way merge functions. I think we don't
> have the same problem in those.
>
> Reported-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  This is one of the two bugs reported by Phillip. It's not tangled with
>  nd/switch-and-restore code changes and I'm sending it separately.

Thanks.

>
>  t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++

This makes it cumbersome to have the same fix in the maintenance track
as t2026 is already in use over there.  Do we need an entirely new test
just to house this new single test?

By the way, I am beginning to like these "in-line" summaries (as
opposed to the --summary at the end), although I admit that it has
been quite a while since its introduction.  Good job, again.

>  unpack-trees.c                     |  2 +-
>  2 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/t/t2026-checkout-force.sh b/t/t2026-checkout-force.sh
> new file mode 100755
> index 0000000000..272ccf533a
> --- /dev/null
> +++ b/t/t2026-checkout-force.sh
> @@ -0,0 +1,26 @@
> +#!/bin/sh
> +
> +test_description='checkout --force'
> +. ./test-lib.sh
> +
> +test_expect_success 'force checking out a conflict' '
> +	echo a >a &&
> +	git add a &&
> +	git commit -ama &&
> +	A_OBJ=$(git rev-parse :a) &&
> +	git branch topic &&
> +	echo b >a &&
> +	git commit -amb &&
> +	B_OBJ=$(git rev-parse :a) &&
> +	git checkout topic &&
> +	echo c >a &&
> +	C_OBJ=$(git hash-object a) &&
> +	git checkout -m master &&
> +	test_cmp_rev :1:a $A_OBJ &&
> +	test_cmp_rev :2:a $B_OBJ &&
> +	test_cmp_rev :3:a $C_OBJ &&
> +	git checkout -f topic &&
> +	test_cmp_rev :a $A_OBJ

So in short, "checkout -f" should have given us an entry for path
"a", taken from the tip of the 'topic' branch, at stage #0 while
switching to that branch, but it didn't?  That would be a nice
summary to have at the beginning of the log message before going
into the implementation detail of how that happens.

> +'
> +
> +test_done
> diff --git a/unpack-trees.c b/unpack-trees.c
> index 22c41a3ba8..1ccd343cad 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,
>  		if (o->update && S_ISGITLINK(old->ce_mode) &&
>  		    should_update_submodules() && !verify_uptodate(old, o))
>  			update |= CE_UPDATE;
> -		add_entry(o, old, update, 0);
> +		add_entry(o, old, update, CE_STAGEMASK);

And the fix is obvious, makes sense and is in line with the
observation you made in the proposed log message.  

Nicely done.

Thanks.

>  		return 0;
>  	}
>  	return merged_entry(a, old, o);

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim
  2019-03-15  8:19         ` Eric Sunshine
  2019-03-15  9:29           ` Duy Nguyen
@ 2019-03-18  4:11           ` Junio C Hamano
  1 sibling, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-18  4:11 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Eckhard Maaß,
	Nguyễn Thái Ngọc Duy, Git List,
	SZEDER Gábor

Eric Sunshine <sunshine@sunshineco.com> writes:

> With git-checkout, it's very easy to accidentally get into a detached
> HEAD state, so it makes some sense to protect newcomers, by default,
> from that accident in git-switch. However, auto-creation of a new
> local branch is not, for a couple reasons, nearly so weighty a matter.
> First, in many cases it may be less likely to happen since it requires
> presence of a corresponding remote tracking branch. Second, it's
> intuitively easy to recover from it: when git-switch reports that it
> created a new branch, though perhaps surprising, the user would
> naturally know to look for a command to "delete a branch".
>
> And, unlike a detached HEAD, which newcomers may mistakenly believe
> lead to irretrievable loss of work, an unexpected branch creation
> carries no such penalty, perceived or real.

I suspect that it actually cuts both ways.  An auto-vivified local
branch 'foo' that happens to have the same name as 'origin/foo' is
still a different line of history UNLESS the user makes sure the
divergence is reconciled with "push" and "pull" (possibly configured
to "pull --rebase", but that is a secondary detail that depends on
the workflow).  Even when 'switch' reports that it created a new
branch, a newcomer would probably not realize that it is "new", and
different from the branch that the remote has.

>> So if it is deemed to be too confusing to dwim in this
>> case, could we add a hint suggesting the command? Something like:
>> "No suitable branch <foo> found, however there is a remote tracking
>> branch <origin/foo> that you can siwtch and create with `git switch
>> --guess foo`" (or maybe the one without guess)?
>
> That could be helpful if git-switch continues to make --no-guess the
> default, but making --guess the default (as it is in git-checkout)
> would likely be even better (for the reasons enumerated above).

I do not have a huge issue with making "--guess" the default, but
then the user may be entitled to help with an advice message when
the command, especially with the implicit "--guess", auto-vivifies a
new local branch based on a remote-tracking branch to stress that
this is a new branch and different from the history the remote side
has.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 00/26] Add new command 'switch'
  2019-03-17 12:58       ` [PATCH v4 00/26] Add new command 'switch' Duy Nguyen
@ 2019-03-18  4:54         ` Junio C Hamano
  2019-03-18 12:13           ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-18  4:54 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, SZEDER Gábor,
	Elijah Newren, Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

Duy Nguyen <pclouds@gmail.com> writes:

> I did forget one thing:
>
> - 'git switch' refuses to switch in the middle of
> rebase/bisect/cherry-pick/... session. Add --ignore-in-progress to
> override this

I think that is a sensible thing to do.  Once I did not realize that
a "checkout" from a failed cherry-pick with the state files left in
the .git/ directory took me to a strange state, causing some later
operation to act on these state files as if a cherry-pick were in
progress (and somehow the shell prompt support did not report it,
perhaps because the copy I use was out of sync with the binary).

Making sure that during a stateful operation the working tree won't
get switched to a different branch is probably a good idea.

I often find "git reset --hard <some other starting point>" after a
failed "git checkout -b new <starting point> && git am -s3c", so I'd
be opposed to a change to refuse a "reset --hard" during the middle
of such a stateful operation, but I do not think of a reason why I
would want to "switch" in the middle.

> Now that I mention this, --ignore-in-progress should probably be part
> of --force too. Hmm...

Probably.  You may want to decline other parts of what --force
allows but when you say --force, --ignore-in-progress should be
implied.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-18  3:58           ` Junio C Hamano
@ 2019-03-18  9:24             ` Duy Nguyen
  2019-03-18  9:40               ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-18  9:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Git Mailing List, Martin Ågren, Eric Sunshine,
	SZEDER Gábor, Elijah Newren

On Mon, Mar 18, 2019 at 10:58 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
> > One-way merge is supposed to take stat info from the index and
> > everything else from the given tree. This implies stage 0 because trees
> > can't have non-zero stages. The add_entry(.., old, ...) call however
> > will keep stage index from the index.
> >
> > This is normally not a problem if the entry from the index is
> > normal (stage #0). But if there is a conflict, we'll get stage #1 entry
> > as "old" and it gets recorded in the final index. Fix it by clearing
> > stage mask.
> >
> > This bug probably comes from b5b425074e (git-read-tree: make one-way
> > merge also honor the "update" flag, 2005-06-07). Before this commit, we
> > may create the final ("dst") index entry from the one in index, but we
> > do clear CE_STAGEMASK.
>
> Wow, good find.  That's an old one.

Credit goes to Phillip for such attention to detail. If I tested this,
I would have stopped after seeing conflict stages collapsed into one
and missed the stage index. In fact I was swearing "what the hell did
he complain about" when looking at his test script's result, until I
realized stage #1 was indeed wrong.

> >
> >  t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++
>
> This makes it cumbersome to have the same fix in the maintenance track
> as t2026 is already in use over there.  Do we need an entirely new test
> just to house this new single test?

I could not find any right file to put it in. I guess I could stick it
in t2023-checkout-m.sh

> > +test_expect_success 'force checking out a conflict' '
> > +     echo a >a &&
> > +     git add a &&
> > +     git commit -ama &&
> > +     A_OBJ=$(git rev-parse :a) &&
> > +     git branch topic &&
> > +     echo b >a &&
> > +     git commit -amb &&
> > +     B_OBJ=$(git rev-parse :a) &&
> > +     git checkout topic &&
> > +     echo c >a &&
> > +     C_OBJ=$(git hash-object a) &&
> > +     git checkout -m master &&
> > +     test_cmp_rev :1:a $A_OBJ &&
> > +     test_cmp_rev :2:a $B_OBJ &&
> > +     test_cmp_rev :3:a $C_OBJ &&
> > +     git checkout -f topic &&
> > +     test_cmp_rev :a $A_OBJ
>
> So in short, "checkout -f" should have given us an entry for path
> "a", taken from the tip of the 'topic' branch, at stage #0 while
> switching to that branch, but it didn't?  That would be a nice
> summary to have at the beginning of the log message before going
> into the implementation detail of how that happens.

OK. And the last line probably should be :0:a to make it clear we're
looking for stage #0.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-18  9:24             ` Duy Nguyen
@ 2019-03-18  9:40               ` Junio C Hamano
  0 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-18  9:40 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Martin Ågren, Eric Sunshine,
	SZEDER Gábor, Elijah Newren

Duy Nguyen <pclouds@gmail.com> writes:

>> > +     test_cmp_rev :1:a $A_OBJ &&
>> > +     test_cmp_rev :2:a $B_OBJ &&
>> > +     test_cmp_rev :3:a $C_OBJ &&
>> > +     git checkout -f topic &&
>> > +     test_cmp_rev :a $A_OBJ
>>
>> So in short, "checkout -f" should have given us an entry for path
>> "a", taken from the tip of the 'topic' branch, at stage #0 while
>> switching to that branch, but it didn't?  That would be a nice
>> summary to have at the beginning of the log message before going
>> into the implementation detail of how that happens.
>
> OK. And the last line probably should be :0:a to make it clear we're
> looking for stage #0.

I would say ":a" is plenty clear that it is looking for a merged
entry without getting replaced with an overly explicit ":0:a", but
":0:a" would not hurt, either.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-17  6:00         ` [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index Nguyễn Thái Ngọc Duy
  2019-03-18  3:58           ` Junio C Hamano
@ 2019-03-18 11:38           ` Nguyễn Thái Ngọc Duy
  2019-03-18 15:40             ` Elijah Newren
  2019-03-19 14:06             ` Phillip Wood
  1 sibling, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-18 11:38 UTC (permalink / raw)
  To: pclouds
  Cc: git, gitster, martin.agren, newren, phillip.wood123, sunshine,
	szeder.dev

Phillip found out that 'git checkout -f <branch>' does not restore
conflict/unmerged files correctly. All tracked files should be taken
from <branch> and all non-zero stages removed. Most of this is true,
except that the final file could be in stage one instead of zero.

"checkout -f" (among other commands) does this with one-way merge, which
is supposed to take stat info from the index and everything else from
the given tree. The add_entry(.., old, ...) call in oneway_merge()
though will keep stage index from the index.

This is normally not a problem if the entry from the index is
normal (stage #0). But if there is a conflict, stage #0 does not exist
and we'll get stage #1 entry as "old" variable, which gets recorded in
the final index. Fix it by clearing stage mask.

This bug probably comes from b5b425074e (git-read-tree: make one-way
merge also honor the "update" flag, 2005-06-07). Before this commit, we
may create the final ("dst") index entry from the one in index, but we
do clear CE_STAGEMASK.

I briefly checked two- and three-way merge functions. I think we don't
have the same problem in those.

Reported-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 v2 updates log message to describe the problem and moves the test to
 t2023-checkout.m.sh

 t/t2023-checkout-m.sh | 24 ++++++++++++++++++++++++
 unpack-trees.c        |  2 +-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
index 7e18985134..fca3f85824 100755
--- a/t/t2023-checkout-m.sh
+++ b/t/t2023-checkout-m.sh
@@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '
 	test_cmp both.txt.conflicted.cleaned both.txt.cleaned
 '
 
+test_expect_success 'force checkout a conflict file creates stage zero entry' '
+	git init co-force &&
+	(
+		cd co-force &&
+		echo a >a &&
+		git add a &&
+		git commit -ama &&
+		A_OBJ=$(git rev-parse :a) &&
+		git branch topic &&
+		echo b >a &&
+		git commit -amb &&
+		B_OBJ=$(git rev-parse :a) &&
+		git checkout topic &&
+		echo c >a &&
+		C_OBJ=$(git hash-object a) &&
+		git checkout -m master &&
+		test_cmp_rev :1:a $A_OBJ &&
+		test_cmp_rev :2:a $B_OBJ &&
+		test_cmp_rev :3:a $C_OBJ &&
+		git checkout -f topic &&
+		test_cmp_rev :0:a $A_OBJ
+	)
+'
+
 test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 22c41a3ba8..1ccd343cad 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,
 		if (o->update && S_ISGITLINK(old->ce_mode) &&
 		    should_update_submodules() && !verify_uptodate(old, o))
 			update |= CE_UPDATE;
-		add_entry(o, old, update, 0);
+		add_entry(o, old, update, CE_STAGEMASK);
 		return 0;
 	}
 	return merged_entry(a, old, o);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-18  2:03                           ` Junio C Hamano
@ 2019-03-18 12:06                             ` Duy Nguyen
  2019-03-18 19:10                             ` Elijah Newren
  1 sibling, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-18 12:06 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Elijah Newren, Eric Sunshine, Phillip Wood, Git Mailing List,
	SZEDER Gábor, Martin Ågren

On Mon, Mar 18, 2019 at 9:03 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> > I don't see why <start-point> even makes sense to use with --orphan;
> > you should error if both are given, IMO.  The point of --orphan is to
> > create some entirely new history.  So, I'd expect "git switch --orphan
> > <new-branch>" to:
> >   * not create refs/heads/<new-branch>
> >   * set HEAD to refs/heads/<new-branch>
> >   * empty all tracked files from the working tree.
> >   * empty the index
> >
> > Alternatively, you could allow <start-point> to be passed with
> > --orphan, adjusting the above steps so that both the index and the
> > working tree are switched to match <start-point>, but ONLY if
> > <start-point> defaults to the empty tree when --orphan is passed.
>
> Do you mean that it's like <start-point> is not really a start-point
> but is an initial tree, i.e.
>
>         switch --orphan --initial-tree=<tree-ish> <new-branch>
>
> is a mere short-hand for
>
>         switch --orhpan <new-branch> &&
>         restore --from-tree=<tree-ish> .
>
> I think that does make sense, but at the same time, I think a major
> reason why people say "checkout does too many things depending on
> the arguments and conext to be easily explained" is exactly due to
> its many "if you give X, it is like writing this longer command
> sequence" short hands, so...

Yeah <start-point> is optional and probably only useful for people who
do a lot of --orphan. I did think if there's any other command that
could be a better host for this orphan branch creation. No such luck.

PS. There's one small difference between 'switch --orphan <new-branch>
<tree-ish>' and your switch/restore combination: the latter refuses if
you have any local changes because it has to remove all tracked files
first, while the former only refuses when the local changes are
outside <tree-ish>. If path A will be tracked by <tree-ish>, then
whatever changes in there may stay.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 00/26] Add new command 'switch'
  2019-03-18  4:54         ` Junio C Hamano
@ 2019-03-18 12:13           ` Duy Nguyen
  2019-03-19  0:43             ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-18 12:13 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, SZEDER Gábor,
	Elijah Newren, Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

On Mon, Mar 18, 2019 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:
> I often find "git reset --hard <some other starting point>" after a

It still bugs me that I need to use this to abort some in-progress
operation. There's "git X --abort" but I would need to find out what X
is first. I would like "git abort" or something (and "git continue")
but haven't really thought that through yet. That direction may also
lead to "git undo" which is another can of worms.

> failed "git checkout -b new <starting point> && git am -s3c", so I'd
> be opposed to a change to refuse a "reset --hard" during the middle
> of such a stateful operation, but I do not think of a reason why I
> would want to "switch" in the middle.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-18 11:38           ` [PATCH v2] " Nguyễn Thái Ngọc Duy
@ 2019-03-18 15:40             ` Elijah Newren
  2019-03-19 14:06             ` Phillip Wood
  1 sibling, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-18 15:40 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Martin Ågren,
	Phillip Wood, Eric Sunshine, SZEDER Gábor

On Mon, Mar 18, 2019 at 4:38 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Phillip found out that 'git checkout -f <branch>' does not restore
> conflict/unmerged files correctly. All tracked files should be taken
> from <branch> and all non-zero stages removed. Most of this is true,
> except that the final file could be in stage one instead of zero.
>
> "checkout -f" (among other commands) does this with one-way merge, which
> is supposed to take stat info from the index and everything else from
> the given tree. The add_entry(.., old, ...) call in oneway_merge()
> though will keep stage index from the index.
>
> This is normally not a problem if the entry from the index is
> normal (stage #0). But if there is a conflict, stage #0 does not exist
> and we'll get stage #1 entry as "old" variable, which gets recorded in
> the final index. Fix it by clearing stage mask.
>
> This bug probably comes from b5b425074e (git-read-tree: make one-way
> merge also honor the "update" flag, 2005-06-07). Before this commit, we
> may create the final ("dst") index entry from the one in index, but we
> do clear CE_STAGEMASK.
>
> I briefly checked two- and three-way merge functions. I think we don't
> have the same problem in those.
>
> Reported-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

This is awesome; thanks for tracking it down and fixing.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 12/26] checkout: split part of it to new command 'switch'
  2019-03-17 12:49       ` [PATCH v4 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-18 16:48         ` Elijah Newren
  0 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-18 16:48 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

On Sun, Mar 17, 2019 at 5:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> "git checkout" doing too many things is a source of confusion for many
> users (and it even bites old timers sometimes). To remedy that, the
> command will be split into two new ones: switch and restore. The good
> old "git checkout" command is still here and will be until all (or most
> of users) are sick of it.
>
> See the new man page for the final design of switch. The actual
> implementation though is still pretty much the same as "git checkout"
> and not completely aligned with the man page. Following patches will
> adjust their behavior to match the man page.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

Looking really good.  Just some minor comments...

> +git-switch(1)
> +=============
> +
> +NAME
> +----
> +git-switch - Switch branches
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'git switch' [<options>] [--guess] <branch>

Should this now use [--no-guess] since --guess is the default?

> +'git switch' [<options>] --detach [<start-point>]
> +'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
> +
> +DESCRIPTION
> +-----------
> +Switch to a specified branch. The working tree and the index are
> +updated to match the branch. All new commits will be added to the tip
> +of this branch.
> +
> +Optionally a new branch could be created with either `-c`, `-C`,
> +automatically from a remote branch of same name (see `--guess`), or
> +detach the working tree from any branch with `--detach`, along with
> +switching.
> +
> +Switching branches does not require a clean index and working tree
> +(i.e. no differences compared to `HEAD`). The operation is aborted
> +however if the switch leads to loss of local changes, unless told
> +otherwise.

Maybe s/otherwise./otherwise with --discard-changes or --merge./, just
for a little extra clarity?

> +-f::
> +--force::
> +       An alias for `--discard-changes`.
> +
> +--discard-changes::
> +       Proceed even if the index or the working tree differs from
> +       `HEAD`. Both the index and working tree are restored to match
> +       the switching target. This is used to throw away local
> +       changes.

It looks like elsewhere you and Junio discussed making --force also
imply --ignore-in-progress.  That option should be moved close to
--force, so that similar options are adjacent, but it also brings up a
question for me:

Is --force an alias for both `--discard-changes` and
`--ignore-in-progress`, or is `--discard-changes` really just another
name for `--force` (i.e. does it too imply `--ignore-in-progress`)?
I'd be tempted to say the former, but I'm curious on others' thoughts.

> +--orphan <new-branch>::
> +       Create a new 'orphan' branch, named `<new-branch>`. If
> +       `<start-point>` is specified, the index and working tree are
> +       adjusted to match it. Otherwise both are adjusted to contain no
> +       tracked files.

Thanks.  I'm still slightly hesitant about whether <start-point>
should be allowed with --orphan; it seems equivalent to me to letting
people have a flag for switching to existing branch A while forcing
the index and working tree to match branch B (defaulting B to HEAD
from before the switch).  Having <start-point> and --orphan together
is just a special case of this idea, and thus allowing those together
seems like it'll cause the more general request to be filed at some
point, and we will already have the precedent of supporting it
somewhere.  This usecase seems to be somewhat esoteric and infrequent,
and could be easily obtained by combining other commands.  I'm worried
that trying to explain this usecase may make the documentation for the
common everyday commands even more complex, and these manpages are
already kind of long.  However, as I said above, that's just a slight
hesitation and maybe I'm just excessively worried about the length of
our manpages for the most common commands.  This new description of
--orphan at least gets the default behavior right.


Great work so far; thanks for working on this.

Elijah

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-18  2:03                           ` Junio C Hamano
  2019-03-18 12:06                             ` Duy Nguyen
@ 2019-03-18 19:10                             ` Elijah Newren
  2019-03-20 12:04                               ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-18 19:10 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Duy Nguyen, Eric Sunshine, Phillip Wood, Git Mailing List,
	SZEDER Gábor, Martin Ågren

On Sun, Mar 17, 2019 at 7:03 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> > I don't see why <start-point> even makes sense to use with --orphan;
> > you should error if both are given, IMO.  The point of --orphan is to
> > create some entirely new history.  So, I'd expect "git switch --orphan
> > <new-branch>" to:
> >   * not create refs/heads/<new-branch>
> >   * set HEAD to refs/heads/<new-branch>
> >   * empty all tracked files from the working tree.
> >   * empty the index
> >
> > Alternatively, you could allow <start-point> to be passed with
> > --orphan, adjusting the above steps so that both the index and the
> > working tree are switched to match <start-point>, but ONLY if
> > <start-point> defaults to the empty tree when --orphan is passed.
>
> Do you mean that it's like <start-point> is not really a start-point
> but is an initial tree, i.e.
>
>         switch --orphan --initial-tree=<tree-ish> <new-branch>
>
> is a mere short-hand for
>
>         switch --orphan <new-branch> &&
>         restore --from-tree=<tree-ish> .

Yes.

> I think that does make sense, but at the same time, I think a major
> reason why people say "checkout does too many things depending on
> the arguments and conext to be easily explained" is exactly due to
> its many "if you give X, it is like writing this longer command
> sequence" short hands, so...

Yes, this is a concern for me too.  I would be happier if we made
--orphan and <start-point> incompatible and avoided the need to
explain how they worked together.  Besides, as you point out, the
wording is bad and should instead be a separate option named
--initial-tree=<tree-ish> which people will then start asking us to
allow them to specify even in cases when --orphan isn't (e.g. `git
switch --initial-tree=HEAD maint`), which is a weird/esoteric usecase
that is probably better served by using separate commands.

> > How about:
> >
> > """
> > Switching and creating branches always involves knowing the
> > <start-point> to begin the new branch from.  Sometimes, people want to
> > create a new branch that does not have any commits yet; --orphan is a
> > flag to allow that.  --orphan overrides the default of HEAD for
> > <start-point> instead causing us to start from an empty history.  The
> > use of --orphan is incompatible with specifying a <start-point>.
> > """
>
> With or without the short-hand to say which initial tree to populate
> the index from, the above description makes sense to me.

Yeah, perhaps "...an empty history with all tracked files removed from
the index and working tree.  The use of..."

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 14/26] switch: add --discard-changes
  2019-03-17 12:49       ` [PATCH v4 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
@ 2019-03-18 23:55         ` Eric Sunshine
  0 siblings, 0 replies; 289+ messages in thread
From: Eric Sunshine @ 2019-03-18 23:55 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, SZEDER Gábor, Elijah Newren,
	Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

On Sun, Mar 17, 2019 at 8:51 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> --discard-changes is a better name than --force for this option since
> it's what really happens. --force is turned to an alias for
> --discard-changes. But it's meant to an alias for potentially more force

s/to an/to be an/

> options in the future.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 00/26] Add new command 'switch'
  2019-03-18 12:13           ` Duy Nguyen
@ 2019-03-19  0:43             ` Junio C Hamano
  0 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-19  0:43 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, SZEDER Gábor,
	Elijah Newren, Eckhard Maaß,
	Martin Ågren, Jacob Keller, Phillip Wood, Andrei Rybak

Duy Nguyen <pclouds@gmail.com> writes:

> On Mon, Mar 18, 2019 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>> I often find "git reset --hard <some other starting point>" after a
>
> It still bugs me that I need to use this to abort some in-progress
> operation.

Note that my comment was *NOT* about aborting the in-progress "am".
What happens in that "I often find" situation is that I identify the
problem's origin, prepare a branch to apply a fix forking from an
old release point that includes the origin, and "am" tells me the
code diverged too much and it cannot be applied, or "am -3" tells me
that the author sent the patch based on some unknown base.  At that
point, I "git reset --hard" to some plausible place the author would
have based the patch (e.g. "master" pushed out last night) and then
say "am" or ("am --no-3") to retry.

So your response is unrelated to what I said, but having said that,
it still deserves a comment.

> ... There's "git X --abort" but I would need to find out what X
> is first. I would like "git abort" or something (and "git continue")
> but haven't really thought that through yet. That direction may also
> lead to "git undo" which is another can of worms.

I rarely had "what was I doing" problem; doesn't the command line
prompt tell you?  It is debatable (and I am pessimistic) if a
general blanket "git abort" makes sense, but the info on the current
status (i.e. "what was the user doing?") it must gather should be
a strict superset as what is needed to give useful info in the
command line prompt, so a good place to start may be to think about
what's missing (not shown) in the prompt and if that is deliberate
omission (avoiding overly verbose prompt) or is coming from lack of
enough information.  Then we may need to do something about the latter
by teaching these commands with state to leave more clue of what we
were in the middle of and what the next step would be.

After thinking about that, the answer might turn out to be to unify
the backend of these commands with state to a single one, or at
least fewer ones, so that we can have better grasp and control over
how these backend engines record state when they give control back
to the user.



^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-14 11:00             ` Phillip Wood
@ 2019-03-19  9:39               ` Nguyễn Thái Ngọc Duy
  2019-03-19 11:24                 ` Phillip Wood
  2019-03-20  0:23                 ` Junio C Hamano
  0 siblings, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-19  9:39 UTC (permalink / raw)
  To: phillip.wood123
  Cc: git, gitster, newren, pclouds, phillip.wood, sunshine, szeder.dev

If you have staged changes in path A and perform 'checkout
--merge' (which could result in conflicts in a totally unrelated path
B), changes in A will be gone. Which is unexpected. We are supposed
to keep all changes, or kick and scream otherwise.

This is the result of how --merge is implemented, from the very first
day in 1be0659efc (checkout: merge local modifications while switching
branches., 2006-01-12):

1. a merge is done, unmerged entries are collected
2. a hard switch to a new branch is done, then unmerged entries added
   back

There is no trivial fix for this. Going with 3-way merge one file at a
time loses rename detection. Going with 3-way merge by trees requires
teaching the algorithm to pick up staged changes. And even if we detect
staged changes with --merge and abort for safety, an option to continue
--merge is very weird. Such an option would keep worktree changes, but
drop staged changes.

Because the problem has been with us since the introduction of --merge
and everybody has been pretty happy (except Phillip, who found this
problem), I'll just take a note here to acknowledge it and wait for
merge wizards to come in and work their magic. There may be a way
forward [1].

[1] CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com

Reported-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 This is my "fix" for Phillip's second problem. I chose to reply here
 because this is where an actual fix was discussed. The test script to
 demonstate it is here

 https://public-inbox.org/git/7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com/

 Documentation/git-checkout.txt | 2 ++
 builtin/checkout.c             | 9 +++++++++
 2 files changed, 11 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index f179b43732..877e5f503a 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -242,6 +242,8 @@ should result in deletion of the path).
 +
 When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
++
+When switching branches with `--merge`, staged changes may be lost.
 
 --conflict=<style>::
 	The same as --merge option above, but changes the way the
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0e6037b296..f95e7975f7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			struct tree *result;
 			struct tree *work;
 			struct merge_options o;
+			struct strbuf sb = STRBUF_INIT;
+
 			if (!opts->merge)
 				return 1;
 
@@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			if (!old_branch_info->commit)
 				return 1;
 
+			if (repo_index_has_changes(the_repository,
+						   get_commit_tree(old_branch_info->commit),
+						   &sb))
+				warning(_("staged changes in the following files may be lost: %s"),
+					sb.buf);
+			strbuf_release(&sb);
+
 			/* Do more real merge */
 
 			/*
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-19  9:39               ` [PATCH] checkout.txt: note about losing staged changes with --merge Nguyễn Thái Ngọc Duy
@ 2019-03-19 11:24                 ` Phillip Wood
  2019-03-20  0:23                 ` Junio C Hamano
  1 sibling, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-19 11:24 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, gitster, newren, phillip.wood, sunshine, szeder.dev

Hi Duy

On 19/03/2019 09:39, Nguyễn Thái Ngọc Duy wrote:
> If you have staged changes in path A and perform 'checkout
> --merge' (which could result in conflicts in a totally unrelated path
> B), changes in A will be gone. Which is unexpected. We are supposed
> to keep all changes, or kick and scream otherwise.
> 
> This is the result of how --merge is implemented, from the very first
> day in 1be0659efc (checkout: merge local modifications while switching
> branches., 2006-01-12):
> 
> 1. a merge is done, unmerged entries are collected
> 2. a hard switch to a new branch is done, then unmerged entries added
>     back
> 
> There is no trivial fix for this. Going with 3-way merge one file at a
> time loses rename detection. Going with 3-way merge by trees requires
> teaching the algorithm to pick up staged changes. And even if we detect
> staged changes with --merge and abort for safety, an option to continue
> --merge is very weird. Such an option would keep worktree changes, but
> drop staged changes.
> 
> Because the problem has been with us since the introduction of --merge
> and everybody has been pretty happy (except Phillip, who found this
> problem), I'll just take a note here to acknowledge it and wait for
> merge wizards to come in and work their magic. There may be a way
> forward [1].
> 
> [1] CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com
> 
> Reported-by: Phillip Wood <phillip.wood123@gmail.com>

I try to use phillip.wood@dunelm.org.uk for git stuff as it shouldn't 
change in the future.

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>   This is my "fix" for Phillip's second problem. I chose to reply here
>   because this is where an actual fix was discussed. The test script to
>   demonstate it is here
> 
>   https://public-inbox.org/git/7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com/
> 
>   Documentation/git-checkout.txt | 2 ++
>   builtin/checkout.c             | 9 +++++++++
>   2 files changed, 11 insertions(+)
> 
> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
> index f179b43732..877e5f503a 100644
> --- a/Documentation/git-checkout.txt
> +++ b/Documentation/git-checkout.txt
> @@ -242,6 +242,8 @@ should result in deletion of the path).
>   +
>   When checking out paths from the index, this option lets you recreate
>   the conflicted merge in the specified paths.
> ++
> +When switching branches with `--merge`, staged changes may be lost.
>   
>   --conflict=<style>::
>   	The same as --merge option above, but changes the way the
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 0e6037b296..f95e7975f7 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
>   			struct tree *result;
>   			struct tree *work;
>   			struct merge_options o;
> +			struct strbuf sb = STRBUF_INIT;
> +
>   			if (!opts->merge)
>   				return 1;
>   
> @@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
>   			if (!old_branch_info->commit)
>   				return 1;
>   
> +			if (repo_index_has_changes(the_repository,
> +						   get_commit_tree(old_branch_info->commit),
> +						   &sb))
> +				warning(_("staged changes in the following files may be lost: %s"),
> +					sb.buf);
> +			strbuf_release(&sb);

Thanks for doing this, I think having some sort of warning is a good 
idea, I wonder if this could be quite noisy though. I guess it depends 
on how many staged changes people have that don't match the new index. 
If we diff against the new tree and only print names that are in both 
lists does that give a definitive list of what will be lost? If it does 
then if there are a lot of files affected then it will still be noisy 
(using columns may help) but at least it will not contain false 
positives. It is more work though, maybe we should just say "staged 
changes may be lost" and leave it at that.

Best Wishes

Phillip

> +
>   			/* Do more real merge */
>   
>   			/*
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-18 11:38           ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  2019-03-18 15:40             ` Elijah Newren
@ 2019-03-19 14:06             ` Phillip Wood
  2019-03-20  0:41               ` Junio C Hamano
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-19 14:06 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, gitster, martin.agren, newren, sunshine, szeder.dev

Hi Duy

Thanks for doing this, one minor comment - I try to use 
phillip.wood@dunelm.org.uk for git as it wont change if I change my 
email provider.

Best Wishes

Phillip

On 18/03/2019 11:38, Nguyễn Thái Ngọc Duy wrote:
> Phillip found out that 'git checkout -f <branch>' does not restore
> conflict/unmerged files correctly. All tracked files should be taken
> from <branch> and all non-zero stages removed. Most of this is true,
> except that the final file could be in stage one instead of zero.
> 
> "checkout -f" (among other commands) does this with one-way merge, which
> is supposed to take stat info from the index and everything else from
> the given tree. The add_entry(.., old, ...) call in oneway_merge()
> though will keep stage index from the index.
> 
> This is normally not a problem if the entry from the index is
> normal (stage #0). But if there is a conflict, stage #0 does not exist
> and we'll get stage #1 entry as "old" variable, which gets recorded in
> the final index. Fix it by clearing stage mask.
> 
> This bug probably comes from b5b425074e (git-read-tree: make one-way
> merge also honor the "update" flag, 2005-06-07). Before this commit, we
> may create the final ("dst") index entry from the one in index, but we
> do clear CE_STAGEMASK.
> 
> I briefly checked two- and three-way merge functions. I think we don't
> have the same problem in those.
> 
> Reported-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>   v2 updates log message to describe the problem and moves the test to
>   t2023-checkout.m.sh
> 
>   t/t2023-checkout-m.sh | 24 ++++++++++++++++++++++++
>   unpack-trees.c        |  2 +-
>   2 files changed, 25 insertions(+), 1 deletion(-)
> 
> diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
> index 7e18985134..fca3f85824 100755
> --- a/t/t2023-checkout-m.sh
> +++ b/t/t2023-checkout-m.sh
> @@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '
>   	test_cmp both.txt.conflicted.cleaned both.txt.cleaned
>   '
>   
> +test_expect_success 'force checkout a conflict file creates stage zero entry' '
> +	git init co-force &&
> +	(
> +		cd co-force &&
> +		echo a >a &&
> +		git add a &&
> +		git commit -ama &&
> +		A_OBJ=$(git rev-parse :a) &&
> +		git branch topic &&
> +		echo b >a &&
> +		git commit -amb &&
> +		B_OBJ=$(git rev-parse :a) &&
> +		git checkout topic &&
> +		echo c >a &&
> +		C_OBJ=$(git hash-object a) &&
> +		git checkout -m master &&
> +		test_cmp_rev :1:a $A_OBJ &&
> +		test_cmp_rev :2:a $B_OBJ &&
> +		test_cmp_rev :3:a $C_OBJ &&
> +		git checkout -f topic &&
> +		test_cmp_rev :0:a $A_OBJ
> +	)
> +'
> +
>   test_done
> diff --git a/unpack-trees.c b/unpack-trees.c
> index 22c41a3ba8..1ccd343cad 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,
>   		if (o->update && S_ISGITLINK(old->ce_mode) &&
>   		    should_update_submodules() && !verify_uptodate(old, o))
>   			update |= CE_UPDATE;
> -		add_entry(o, old, update, 0);
> +		add_entry(o, old, update, CE_STAGEMASK);
>   		return 0;
>   	}
>   	return merged_entry(a, old, o);
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-19  9:39               ` [PATCH] checkout.txt: note about losing staged changes with --merge Nguyễn Thái Ngọc Duy
  2019-03-19 11:24                 ` Phillip Wood
@ 2019-03-20  0:23                 ` Junio C Hamano
  2019-03-20  0:40                   ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-20  0:23 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: phillip.wood123, git, newren, phillip.wood, sunshine, szeder.dev

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> If you have staged changes in path A and perform 'checkout
> --merge' (which could result in conflicts in a totally unrelated path
> B), changes in A will be gone. Which is unexpected. We are supposed
> to keep all changes, or kick and scream otherwise.
>
> This is the result of how --merge is implemented, from the very first
> day in 1be0659efc (checkout: merge local modifications while switching
> branches., 2006-01-12):
>
> 1. a merge is done, unmerged entries are collected
> 2. a hard switch to a new branch is done, then unmerged entries added
>    back
>
> There is no trivial fix for this. Going with 3-way merge one file at a
> time loses rename detection. Going with 3-way merge by trees requires
> teaching the algorithm to pick up staged changes. And even if we detect
> staged changes with --merge and abort for safety, an option to continue
> --merge is very weird. Such an option would keep worktree changes, but
> drop staged changes.

I think "checkout -m <otherbranch>" with a dirty index should refuse
to run; there is nothing to "continue" after such a failure, so I am
not sure what you mean by "an option to continue" (iow, I do not see
a need for such an option, and if that option makes the whole notion
strange, we can just decide not to have it, can't we?).

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20  0:23                 ` Junio C Hamano
@ 2019-03-20  0:40                   ` Duy Nguyen
  2019-03-20  1:19                     ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-20  0:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Git Mailing List, Elijah Newren, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

On Wed, Mar 20, 2019 at 7:24 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
> > If you have staged changes in path A and perform 'checkout
> > --merge' (which could result in conflicts in a totally unrelated path
> > B), changes in A will be gone. Which is unexpected. We are supposed
> > to keep all changes, or kick and scream otherwise.
> >
> > This is the result of how --merge is implemented, from the very first
> > day in 1be0659efc (checkout: merge local modifications while switching
> > branches., 2006-01-12):
> >
> > 1. a merge is done, unmerged entries are collected
> > 2. a hard switch to a new branch is done, then unmerged entries added
> >    back
> >
> > There is no trivial fix for this. Going with 3-way merge one file at a
> > time loses rename detection. Going with 3-way merge by trees requires
> > teaching the algorithm to pick up staged changes. And even if we detect
> > staged changes with --merge and abort for safety, an option to continue
> > --merge is very weird. Such an option would keep worktree changes, but
> > drop staged changes.
>
> I think "checkout -m <otherbranch>" with a dirty index should refuse
> to run; there is nothing to "continue" after such a failure, so I am
> not sure what you mean by "an option to continue" (iow, I do not see
> a need for such an option, and if that option makes the whole notion
> strange, we can just decide not to have it, can't we?).

We have --force to continue even when we have local changes, which
will be overwritten. I was thinking a similar option which gives us
permission to destroy staged changes.

Refusing to run fails the test suite though (I tried that even before
this patch), in t7201.10, "switch to another branch while carrying a
deletion", because of this line

    git rm two

-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-19 14:06             ` Phillip Wood
@ 2019-03-20  0:41               ` Junio C Hamano
  2019-03-20  9:50                 ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-20  0:41 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Nguyễn Thái Ngọc Duy, git, martin.agren, newren,
	sunshine, szeder.dev

Phillip Wood <phillip.wood123@gmail.com> writes:

> Thanks for doing this, one minor comment - I try to use
> phillip.wood@dunelm.org.uk for git as it wont change if I change my
> email provider.

You mean something like this?

 .mailmap | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.mailmap b/.mailmap
index 82cd0569d5..6e137576ec 100644
--- a/.mailmap
+++ b/.mailmap
@@ -212,6 +212,7 @@ Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>
 Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>
 Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>
 Philippe Bruhat <book@cpan.org>
+Phillip Wood <phillip.wood@dunelm.org.uk> <phillip.wood123@gmail.com>
 Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>
 Randall S. Becker <randall.becker@nexbridge.ca> <rsbecker@nexbridge.com>

^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20  0:40                   ` Duy Nguyen
@ 2019-03-20  1:19                     ` Junio C Hamano
  2019-03-20  1:22                       ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-20  1:19 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Elijah Newren, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

Duy Nguyen <pclouds@gmail.com> writes:

>> I think "checkout -m <otherbranch>" with a dirty index should refuse
>> to run; there is nothing to "continue" after such a failure, so I am
>> not sure what you mean by "an option to continue" (iow, I do not see
>> a need for such an option, and if that option makes the whole notion
>> strange, we can just decide not to have it, can't we?).
>
> We have --force to continue even when we have local changes, which
> will be overwritten. I was thinking a similar option which gives us
> permission to destroy staged changes.

Ah, then that is not "checkout --continue", but "checkout --force
-m"?  That sounds sensible, and should behave as if "checkout -f
HEAD && checkout -m <otherbranch>" was done, with respect to local
changes, I would think.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20  1:19                     ` Junio C Hamano
@ 2019-03-20  1:22                       ` Duy Nguyen
  2019-03-20  1:50                         ` Junio C Hamano
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-20  1:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Git Mailing List, Elijah Newren, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

On Wed, Mar 20, 2019 at 8:19 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> >> I think "checkout -m <otherbranch>" with a dirty index should refuse
> >> to run; there is nothing to "continue" after such a failure, so I am
> >> not sure what you mean by "an option to continue" (iow, I do not see
> >> a need for such an option, and if that option makes the whole notion
> >> strange, we can just decide not to have it, can't we?).
> >
> > We have --force to continue even when we have local changes, which
> > will be overwritten. I was thinking a similar option which gives us
> > permission to destroy staged changes.
>
> Ah, then that is not "checkout --continue", but "checkout --force
> -m"?  That sounds sensible, and should behave as if "checkout -f
> HEAD && checkout -m <otherbranch>" was done, with respect to local
> changes, I would think.

Kinda. But "--force --merge" makes no sense. --force discards all
local changes by definition, which means you can't have conflicts and
will not need --merge. I think this is the reason why we die() out
when both are specified. So we need something like
--discard-staged-changes-only...
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20  1:22                       ` Duy Nguyen
@ 2019-03-20  1:50                         ` Junio C Hamano
  2019-03-20 13:53                           ` Elijah Newren
  0 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-20  1:50 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Elijah Newren, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

Duy Nguyen <pclouds@gmail.com> writes:

> Kinda. But "--force --merge" makes no sense. --force discards all
> local changes by definition, which means you can't have conflicts and
> will not need --merge. I think this is the reason why we die() out
> when both are specified. So we need something like
> --discard-staged-changes-only...

At that point, I would have to say that we do not need anything.
The use case is already covered with "git reset && git checkout -m",
isn't it?

Thanks.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index
  2019-03-20  0:41               ` Junio C Hamano
@ 2019-03-20  9:50                 ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-20  9:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Git Mailing List, Martin Ågren, Elijah Newren,
	Eric Sunshine, SZEDER Gábor

On Wed, Mar 20, 2019 at 7:41 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> > Thanks for doing this, one minor comment - I try to use
> > phillip.wood@dunelm.org.uk for git as it wont change if I change my
> > email provider.
>
> You mean something like this?

I think he meant fixing the Reported-by: line. But since there's at
least another commit in 'next' with the 123@gmail address, it's
probably best to update .mailmap too (and maybe still fix the
Reported-by: in this patch).

>  .mailmap | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/.mailmap b/.mailmap
> index 82cd0569d5..6e137576ec 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -212,6 +212,7 @@ Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>
>  Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>
>  Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>
>  Philippe Bruhat <book@cpan.org>
> +Phillip Wood <phillip.wood@dunelm.org.uk> <phillip.wood123@gmail.com>
>  Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
>  Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>
>  Randall S. Becker <randall.becker@nexbridge.ca> <rsbecker@nexbridge.com>



-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v4 22/26] switch: reject if some operation is in progress
  2019-03-17 12:49       ` [PATCH v4 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
@ 2019-03-20 11:16         ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-20 11:16 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, gitster, sunshine, szeder.dev, Elijah Newren,
	eckhard.s.maass, Martin Ågren, jacob.keller, rybak.a.v

Hi Duy

The new switch command is shaping up nicely

On 17/03/2019 12:49, Nguyễn Thái Ngọc Duy wrote:
> Unless you know what you're doing, switching to another branch to do
> something then switching back could be confusing. Worse, you may even
> forget that you're in the middle of something. By the time you realize,
> you may have done a ton of work and it gets harder to go back.
> 
> The new option --ignore-in-progress is added to override this check.
> However it's probably just safer to create a separate worktree for
> that separate thing you want to work on and leave this worktree
> alone (unless of course creating or preparing worktrees are not cheap).
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 2aeaac092d..21a585eaa9 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -24,6 +24,7 @@
>  #include "tree.h"
>  #include "tree-walk.h"
>  #include "unpack-trees.h"
> +#include "wt-status.h"
>  #include "xdiff-interface.h"
>  
>  static const char * const checkout_usage[] = {
> @@ -56,6 +57,7 @@ struct checkout_opts {
>  	int accept_pathspec;
>  	int switch_branch_doing_nothing_is_ok;
>  	int only_merge_on_switching_branches;
> +	int can_switch_when_in_progress;
>  
>  	const char *new_branch;
>  	const char *new_branch_force;
> @@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,
>  	    !new_branch_info->path)
>  		die_expecting_a_branch(new_branch_info);
>  
> +	if (!opts->can_switch_when_in_progress) {
> +		struct wt_status_state state;
> +
> +		memset(&state, 0, sizeof(state));
> +		wt_status_get_state(the_repository, &state, 0);
> +
> +		if (state.merge_in_progress)
> +			die(_("cannot switch branch while merging"));
> +		if (state.am_in_progress)
> +			die(_("cannot switch branch in the middle of an am session"));
> +		if (state.rebase_in_progress || state.rebase_in_progress)

I think one of those should be state.rebase_interactive_in_progress

Best Wishes

Phillip

> +			die(_("cannot switch branch while rebasing"));
> +		if (state.cherry_pick_in_progress)
> +			die(_("cannot switch branch while cherry-picking"));
> +		if (state.revert_in_progress)
> +			die(_("cannot switch branch while reverting"));
> +		if (state.bisect_in_progress)
> +			die(_("cannot switch branch while bisecting"));
> +	}
> +
>  	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
>  	    !opts->ignore_other_worktrees) {
>  		int flag;
> @@ -1514,6 +1536,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>  	opts.only_merge_on_switching_branches = 0;
>  	opts.accept_pathspec = 1;
>  	opts.implicit_detach = 1;
> +	opts.can_switch_when_in_progress = 1;
>  
>  	options = parse_options_dup(checkout_options);
>  	options = add_common_options(&opts, options);
> @@ -1539,6 +1562,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>  			 N_("second guess 'git switch <no-such-branch>'")),
>  		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
>  			 N_("throw away local modifications")),
> +		OPT_BOOL(0, "ignore-in-progress", &opts.can_switch_when_in_progress,
> +			 N_("allow to switch branch when some operation is still in progress")),
>  		OPT_END()
>  	};
>  	int ret;
> @@ -1549,6 +1574,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>  	opts.switch_branch_doing_nothing_is_ok = 0;
>  	opts.only_merge_on_switching_branches = 1;
>  	opts.implicit_detach = 0;
> +	opts.can_switch_when_in_progress = 0;
>  
>  	options = parse_options_dup(switch_options);
>  	options = add_common_options(&opts, options);
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-18 19:10                             ` Elijah Newren
@ 2019-03-20 12:04                               ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-20 12:04 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Junio C Hamano, Eric Sunshine, Phillip Wood, Git Mailing List,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 19, 2019 at 2:10 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Sun, Mar 17, 2019 at 7:03 PM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > Elijah Newren <newren@gmail.com> writes:
> >
> > > I don't see why <start-point> even makes sense to use with --orphan;
> > > you should error if both are given, IMO.  The point of --orphan is to
> > > create some entirely new history.  So, I'd expect "git switch --orphan
> > > <new-branch>" to:
> > >   * not create refs/heads/<new-branch>
> > >   * set HEAD to refs/heads/<new-branch>
> > >   * empty all tracked files from the working tree.
> > >   * empty the index
> > >
> > > Alternatively, you could allow <start-point> to be passed with
> > > --orphan, adjusting the above steps so that both the index and the
> > > working tree are switched to match <start-point>, but ONLY if
> > > <start-point> defaults to the empty tree when --orphan is passed.
> >
> > Do you mean that it's like <start-point> is not really a start-point
> > but is an initial tree, i.e.
> >
> >         switch --orphan --initial-tree=<tree-ish> <new-branch>
> >
> > is a mere short-hand for
> >
> >         switch --orphan <new-branch> &&
> >         restore --from-tree=<tree-ish> .
>
> Yes.
>
> > I think that does make sense, but at the same time, I think a major
> > reason why people say "checkout does too many things depending on
> > the arguments and conext to be easily explained" is exactly due to
> > its many "if you give X, it is like writing this longer command
> > sequence" short hands, so...
>
> Yes, this is a concern for me too.  I would be happier if we made
> --orphan and <start-point> incompatible and avoided the need to
> explain how they worked together.  Besides, as you point out, the
> wording is bad and should instead be a separate option named
> --initial-tree=<tree-ish> which people will then start asking us to
> allow them to specify even in cases when --orphan isn't (e.g. `git
> switch --initial-tree=HEAD maint`), which is a weird/esoteric usecase
> that is probably better served by using separate commands.

OK let's reject '--orphan <new-branch> <initial-tree>' then. It's not
much work and when people come back complaining about it not working,
we'll know more and may reconsider then.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20  1:50                         ` Junio C Hamano
@ 2019-03-20 13:53                           ` Elijah Newren
  2019-03-20 13:57                             ` Duy Nguyen
  2019-03-21  0:38                             ` Junio C Hamano
  0 siblings, 2 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-20 13:53 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

On Tue, Mar 19, 2019 at 7:50 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> > Kinda. But "--force --merge" makes no sense. --force discards all
> > local changes by definition, which means you can't have conflicts and
> > will not need --merge. I think this is the reason why we die() out
> > when both are specified. So we need something like
> > --discard-staged-changes-only...
>
> At that point, I would have to say that we do not need anything.
> The use case is already covered with "git reset && git checkout -m",
> isn't it?

I guess the problem is just that 'git checkout -m' has not refused to
run with either a dirty index or a dirty working tree, and if both are
dirty (making us require more of a four-way merge), then our three-way
merge has to have some kind of casualty in the implementation for at
least some case.  The current casualty as highlighted by Philip is
that newly staged files before the 'checkout -m' become untracked and
any carefully staged pieces before that command are lost amongst the
unstaged changes again even if there weren't any conflicts.

One solution is to just accept and document or warn about this
shortcoming for now as Duy did in his patch.  Another is to do as you
mentioned earlier in this thread when you stated 'I think "checkout -m
<otherbranch>" with a dirty index should refuse to run'.  Duy linked
to a third option that I outlined in his commit message, though it'd
require a bit more capability from merge-recursive than we have today.

So, I think we do need something (eventually at least).  Would you
prefer we dropped this patch from Duy and instead made 'checkout -m'
abort when the index is dirty?

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20 13:53                           ` Elijah Newren
@ 2019-03-20 13:57                             ` Duy Nguyen
  2019-03-21 13:46                               ` Elijah Newren
  2019-03-21  0:38                             ` Junio C Hamano
  1 sibling, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-20 13:57 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Junio C Hamano, Phillip Wood, Git Mailing List, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

On Wed, Mar 20, 2019 at 8:53 PM Elijah Newren <newren@gmail.com> wrote:
> So, I think we do need something (eventually at least).  Would you
> prefer we dropped this patch from Duy and instead made 'checkout -m'
> abort when the index is dirty?

I have no problem with this. Still scratching my head wondering if
t7201-co.sh has a slightly incorrect setup, or aborting is actually
wrong. You're probably a better person to understand that test case
;-)
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20 13:53                           ` Elijah Newren
  2019-03-20 13:57                             ` Duy Nguyen
@ 2019-03-21  0:38                             ` Junio C Hamano
  1 sibling, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-03-21  0:38 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

Elijah Newren <newren@gmail.com> writes:

> On Tue, Mar 19, 2019 at 7:50 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Duy Nguyen <pclouds@gmail.com> writes:
>>
>> > Kinda. But "--force --merge" makes no sense. --force discards all
>> > local changes by definition, which means you can't have conflicts and
>> > will not need --merge. I think this is the reason why we die() out
>> > when both are specified. So we need something like
>> > --discard-staged-changes-only...
>>
>> At that point, I would have to say that we do not need anything.
>> The use case is already covered with "git reset && git checkout -m",
>> isn't it?
>
> I guess the problem is just that 'git checkout -m' has not refused to
> run with either a dirty index or a dirty working tree, and if both are
> dirty (making us require more of a four-way merge), then our three-way
> merge has to ...

I didn't actually mean "nothing to do here" relative to the current
code; instead, I meant "nothing more than just stop when the index
has updates" (which is hard to read from the above quoted part, as
"Kinda." is a response in a discussion started with my "checkout -m
should probably refuse to do anything when the index is dirty").

> So, I think we do need something (eventually at least).  Would you
> prefer we dropped this patch from Duy and instead made 'checkout -m'
> abort when the index is dirty?

Let's go with the doc update first, as the patch has already
written.  I think in the longer term, just aborting when the index
is dirty would be a vast improvement over the status quo + a doc
update and is a good place to stop.

Thanks.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v5 00/26] Add new command 'switch'
  2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
                         ` (26 preceding siblings ...)
  2019-03-17 12:58       ` [PATCH v4 00/26] Add new command 'switch' Duy Nguyen
@ 2019-03-21 13:16       ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
                           ` (28 more replies)
  27 siblings, 29 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This adds a new command 'git-switch' as the half-replacement for
'git-checkout'. Jump to 12/26 as the starting point. The other half is
git-restore, which is dealt with separately.

The diff delta is shrinking nicely. The two main changes are

- '--orphan <new-branch> <initial-tree>' is no longer accepted
- --force implies --ignore-in-progress

The rest is the usual typo and bug fixes.

The two "todo" items from v4 will be handled separately and will not
be mentioned again in v6 (if there is one):

 - The 'checkout -m' losing staged changes problem has already been
discussed elsewhere and I'm still trying to see if I can improve it.

 - I have some work in progress for improving tracking branch error
message, but it's unlikely to conflict badly with this series.

Nguyễn Thái Ngọc Duy (26):
  git-checkout.txt: spell out --no-option
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  git-checkout.txt: fix monospace typeset
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: advice how to get out of detached HEAD mode
  checkout: keep most #include sorted
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command 'switch'
  switch: better names for -b and -B
  switch: add --discard-changes
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: implicit dwim, use --no-guess to disable it
  switch: no worktree status unless real branch switch happens
  switch: reject if some operation is in progress
  switch: make --orphan switch to an empty tree
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |  12 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                | 221 ++++---
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   5 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt (new)            | 276 ++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  19 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/gitworkflows.txt                |   3 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  56 +-
 Makefile                                      |   1 +
 advice.c                                      |  17 +-
 builtin.h                                     |   1 +
 builtin/checkout.c                            | 605 ++++++++++--------
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  37 +-
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   1 +
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |  28 +-
 t/t2060-switch.sh (new +x)                    |  98 +++
 39 files changed, 1051 insertions(+), 493 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh

Range-diff dựa trên v4:
 1:  535dc1f310 !  1:  7bcb4b0ff8 doc: document --overwrite-ignore
    @@ -22,20 +22,7 @@
     +
      --recurse-submodules::
      --no-recurse-submodules::
    --	Using --recurse-submodules will update the content of all initialized
    -+	Using `--recurse-submodules` will update the content of all initialized
    - 	submodules according to the commit recorded in the superproject. If
    - 	local modifications in a submodule would be overwritten the checkout
    --	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
    -+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
    - 	is used, the work trees of submodules will not be updated.
    --	Just like linkgit:git-submodule[1], this will detach the
    --	submodules HEAD.
    -+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    -+	submodule.
    - 
    - --no-guess::
    - 	Do not attempt to create a branch if a remote tracking branch
    + 	Using --recurse-submodules will update the content of all initialized
     
      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
      --- a/Documentation/git-merge.txt
 2:  b6305d2871 !  2:  f2d77152eb git-checkout.txt: fix monospace typeset
    @@ -4,8 +4,7 @@
     
         Add backticks where we have none, replace single quotes with backticks
         and replace double-quotes. Drop double-quotes from nested constructions
    -    such as `"@{-1}"`. Add a missing possessive apostrophe after the word
    -    "submodules" while at it.
    +    such as `"@{-1}"`.
     
         Helped-by: Martin Ågren <martin.agren@gmail.com>
         Signed-off-by: Martin Ågren <martin.agren@gmail.com>
    @@ -207,6 +206,24 @@
      This means that you can use `git checkout -p` to selectively discard
      edits from your current working tree. See the ``Interactive Mode''
     @@
    + 
    + --recurse-submodules::
    + --no-recurse-submodules::
    +-	Using --recurse-submodules will update the content of all initialized
    ++	Using `--recurse-submodules` will update the content of all initialized
    + 	submodules according to the commit recorded in the superproject. If
    + 	local modifications in a submodule would be overwritten the checkout
    +-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
    ++	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
    + 	is used, the work trees of submodules will not be updated.
    +-	Just like linkgit:git-submodule[1], this will detach the
    +-	submodules HEAD.
    ++	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    ++	submodule.
    + 
    + --no-guess::
    + 	Do not attempt to create a branch if a remote tracking branch
    +@@
      	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
 3:  bb56e45457 =  3:  cbe630fb4a t: rename t2014-switch.sh to t2014-checkout-switch.sh
 4:  123392757a =  4:  6bc8a1052f checkout: advice how to get out of detached HEAD mode
 5:  d1ec6b4ce0 =  5:  0bb2aefb85 checkout: keep most #include sorted
 6:  4b1742672b =  6:  ace82aa26f checkout: factor out some code in parse_branchname_arg()
 7:  e0bcc3a4dd =  7:  6e13efcba8 checkout: make "opts" in cmd_checkout() a pointer
 8:  ca5b4d7db0 =  8:  f61a042eb8 checkout: move 'confict_style' and 'dwim_..' to checkout_opts
 9:  13c03997f0 =  9:  972cebc568 checkout: split options[] array in three pieces
10:  24d532b276 ! 10:  970c727b24 checkout: split part of it to new command 'switch'
    @@ -147,9 +147,10 @@
     +SYNOPSIS
     +--------
     +[verse]
    -+'git switch' [<options>] [--guess] <branch>
    ++'git switch' [<options>] [--no-guess] <branch>
     +'git switch' [<options>] --detach [<start-point>]
    -+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]
    ++'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]
    ++'git switch' [<options>] --orphan <new-branch>
     +
     +DESCRIPTION
     +-----------
    @@ -164,8 +165,8 @@
     +
     +Switching branches does not require a clean index and working tree
     +(i.e. no differences compared to `HEAD`). The operation is aborted
    -+however if the switch leads to loss of local changes, unless told
    -+otherwise.
    ++however if the operation leads to loss of local changes, unless told
    ++otherwise with `--discard-changes` or `--merge`.
     +
     +OPTIONS
     +-------
    @@ -183,7 +184,7 @@
     +	from some other point.)
     ++
     +You can use the `@{-N}` syntax to refer to the N-th last
    -+branch/commit switched to "git switch" or "git checkout"
    ++branch/commit switched to using "git switch" or "git checkout"
     +operation. You may also specify `-` which is synonymous to `@{-1}`.
     +This is often used to switch quickly between two branches, or to undo
     +a branch switch by mistake.
    @@ -242,7 +243,7 @@
     +
     +-f::
     +--force::
    -+	An alias for `--discard-changes`.
    ++	An alias for `--discard-changes` and `--ignore-in-progress`.
     +
     +--discard-changes::
     +	Proceed even if the index or the working tree differs from
    @@ -250,6 +251,11 @@
     +	the switching target. This is used to throw away local
     +	changes.
     +
    ++--ignore-in-progress::
    ++	`git switch` by default refuses when some operation is in
    ++	progress (e.g. "git rebase", "git am" ...). This option
    ++	overrides this safety check and allows switching.
    ++
     +-m::
     +--merge::
     +	If you have local modifications to one or more files that are
    @@ -304,10 +310,8 @@
     +	`branch.autoSetupMerge` configuration variable is true.
     +
     +--orphan <new-branch>::
    -+	Create a new 'orphan' branch, named `<new-branch>`. If
    -+	`<start-point>` is specified, the index and working tree are
    -+	adjusted to match it. Otherwise both are adjusted to contain no
    -+	tracked files.
    ++	Create a new 'orphan' branch, named `<new-branch>`. All
    ++	tracked files are removed.
     +
     +--ignore-other-worktrees::
     +	`git switch` refuses when the wanted ref is already
    @@ -315,11 +319,6 @@
     +	the ref out anyway. In other words, the ref can be held by
     +	more than one worktree.
     +
    -+--ignore-in-progress::
    -+	`git switch` by default refuses when some operation is in
    -+	progress (e.g. "git rebase", "git am" ...). This option
    -+	overrides this safety check and allows switching.
    -+
     +--recurse-submodules::
     +--no-recurse-submodules::
     +	Using `--recurse-submodules` will update the content of all initialized
11:  c966bacfcc = 11:  676f5df0fd switch: better names for -b and -B
12:  bdb88bf9a9 ! 12:  4e37c0659e switch: add --discard-changes
    @@ -4,8 +4,8 @@
     
         --discard-changes is a better name than --force for this option since
         it's what really happens. --force is turned to an alias for
    -    --discard-changes. But it's meant to an alias for potentially more force
    -    options in the future.
    +    --discard-changes. But it's meant to be an alias for potentially more
    +    force options in the future.
     
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
13:  d5fe7f4bd0 = 13:  a57208d137 switch: remove -l
14:  3bce4c521e = 14:  3de6f95bf2 switch: stop accepting pathspec
15:  dad0063fc4 = 15:  ad225517cd switch: reject "do nothing" case
16:  41ca042917 = 16:  583cfd5cc4 switch: only allow explicit detached HEAD
17:  a0b9f1b285 = 17:  1c5aee658d switch: add short option for --detach
18:  3d254df104 = 18:  d942ac52e2 switch: implicit dwim, use --no-guess to disable it
19:  c6ea203f36 = 19:  37eb152c0d switch: no worktree status unless real branch switch happens
20:  5c4effc7fd ! 20:  50d6768afd switch: reject if some operation is in progress
    @@ -12,6 +12,9 @@
         that separate thing you want to work on and leave this worktree
         alone (unless of course creating or preparing worktrees are not cheap).
     
    +    --force is updated to also imply --ignore-in-progress because it is
    +    supposed to be the "just do your things and don't bother me" option.
    +
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
      +++ b/builtin/checkout.c
    @@ -45,7 +48,7 @@
     +			die(_("cannot switch branch while merging"));
     +		if (state.am_in_progress)
     +			die(_("cannot switch branch in the middle of an am session"));
    -+		if (state.rebase_in_progress || state.rebase_in_progress)
    ++		if (state.rebase_interactive_in_progress || state.rebase_in_progress)
     +			die(_("cannot switch branch while rebasing"));
     +		if (state.cherry_pick_in_progress)
     +			die(_("cannot switch branch while cherry-picking"));
    @@ -58,6 +61,18 @@
      	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
      	    !opts->ignore_other_worktrees) {
      		int flag;
    +@@
    + 		opts->merge = 1; /* implied */
    + 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
    + 	}
    +-	if (opts->force)
    ++	if (opts->force) {
    + 		opts->discard_changes = 1;
    ++		opts->can_switch_when_in_progress = 1;
    ++	}
    + 
    + 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
    + 		die(_("-b, -B and --orphan are mutually exclusive"));
     @@
      	opts.only_merge_on_switching_branches = 0;
      	opts.accept_pathspec = 1;
21:  a4afe6a999 ! 21:  af8bb710c8 switch: --orphan defaults to empty tree as HEAD
    @@ -1,15 +1,16 @@
     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
     
    -    switch: --orphan defaults to empty tree as HEAD
    +    switch: make --orphan switch to an empty tree
     
         Switching and creating branches always involves knowing the
         <start-point> to begin the new branch from. Sometimes, people want to
         create a new branch that does not have any commits yet; --orphan is a
         flag to allow that.
     
    -    If <start-point> is not specified, instead of leaving index/worktree
    -    unchanged, default to "empty tree" starting point, allowing a clean
    -    start.
    +    --orphan overrides the default of HEAD for <start-point> instead causing
    +    us to start from an empty history with all tracked files removed from
    +    the index and working tree. The use of --orphan is incompatible with
    +    specifying a <start-point>.
     
         A note on the implementation. An alternative is just create a dummy
         commit in-core with empty tree and switch to it. But there's a chance
    @@ -23,7 +24,7 @@
      	int switch_branch_doing_nothing_is_ok;
      	int only_merge_on_switching_branches;
      	int can_switch_when_in_progress;
    -+	int orphan_default_empty_tree;
    ++	int orphan_from_empty_tree;
      
      	const char *new_branch;
      	const char *new_branch_force;
    @@ -38,9 +39,11 @@
      		return error(_("index file corrupt"));
      
      	resolve_undo_clear();
    -+	if (opts->orphan_default_empty_tree && !new_branch_info->commit)
    ++	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
    ++		if (new_branch_info->commit)
    ++			BUG("'switch --orphan' should never accept a commit as starting point");
     +		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
    -+	else
    ++	} else
     +		new_tree = get_commit_tree(new_branch_info->commit);
      	if (opts->discard_changes) {
     -		ret = reset_tree(get_commit_tree(new_branch_info->commit),
    @@ -106,23 +109,34 @@
      	if (prepare_revision_walk(&revs))
      		die(_("internal error in revision walk"));
     @@
    + 	if (old_branch_info.path)
    + 		skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
      
    - 		if (opts->only_merge_on_switching_branches)
    - 			do_merge = 0;
    ++	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
    ++		if (new_branch_info->name)
    ++			BUG("'switch --orphan' should never accept a commit as starting point");
    ++		new_branch_info->commit = NULL;
    ++		new_branch_info->name = "(empty)";
    ++		do_merge = 1;
    ++	}
     +
    -+		if (opts->new_orphan_branch && opts->orphan_default_empty_tree) {
    -+			new_branch_info->commit = NULL;
    -+			new_branch_info->name = "(empty)";
    -+			do_merge = 1;
    -+		}
    - 	}
    - 
    - 	if (do_merge) {
    + 	if (!new_branch_info->name) {
    + 		new_branch_info->name = "HEAD";
    + 		new_branch_info->commit = old_branch_info.commit;
    +@@
    + 	if (opts->new_orphan_branch) {
    + 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
    + 			die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
    ++		if (opts->orphan_from_empty_tree && new_branch_info->name)
    ++			die(_("'%s' cannot take <start-point>"), "--orphan");
    + 	} else if (opts->force_detach) {
    + 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
    + 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
     @@
      	opts.accept_pathspec = 1;
      	opts.implicit_detach = 1;
      	opts.can_switch_when_in_progress = 1;
    -+	opts.orphan_default_empty_tree = 0;
    ++	opts.orphan_from_empty_tree = 0;
      
      	options = parse_options_dup(checkout_options);
      	options = add_common_options(&opts, options);
    @@ -130,7 +144,7 @@
      	opts.only_merge_on_switching_branches = 1;
      	opts.implicit_detach = 0;
      	opts.can_switch_when_in_progress = 0;
    -+	opts.orphan_default_empty_tree = 1;
    ++	opts.orphan_from_empty_tree = 1;
      
      	options = parse_options_dup(switch_options);
      	options = add_common_options(&opts, options);
22:  6cca78f835 ! 22:  4a293a3d53 t: add tests for switch
    @@ -67,6 +67,7 @@
     +
     +test_expect_success 'new orphan branch from empty' '
     +	test_when_finished git switch master &&
    ++	test_must_fail git switch --orphan new-orphan HEAD &&
     +	git switch --orphan new-orphan &&
     +	test_commit orphan &&
     +	git cat-file commit refs/heads/new-orphan >commit &&
23:  e5e6f9d6f1 = 23:  b10290b378 completion: support switch
24:  807e8bc50b = 24:  67c5175e5f doc: promote "git switch"
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v5 01/26] git-checkout.txt: spell out --no-option
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                           ` (27 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

It's easier to search for and also less cryptic.
---
 Documentation/git-checkout.txt | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index f179b43732..99c8c0dc0f 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,7 +118,8 @@ OPTIONS
 --quiet::
 	Quiet, suppress feedback messages.
 
---[no-]progress::
+--progress::
+--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
@@ -262,7 +263,7 @@ 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.
+`--overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
---[no-]recurse-submodules::
+--recurse-submodules::
+--no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also
 	Do not attempt to create a branch if a remote tracking branch
 	of the same name exists.
 
---[no-]overlay::
+--overlay::
+--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
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 02/26] git-checkout.txt: fix one syntax line
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                           ` (26 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 99c8c0dc0f..28817cfa41 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 03/26] doc: document --overwrite-ignore
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
                           ` (25 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.
---
 Documentation/git-checkout.txt | 6 ++++++
 Documentation/git-merge.txt    | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 28817cfa41..5280d1f9ed 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort
+	the operation when the new branch contains ignored files.
+
 --recurse-submodules::
 --no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..6a9163d8fe 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,11 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 04/26] git-checkout.txt: fix monospace typeset
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (2 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                           ` (24 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Add backticks where we have none, replace single quotes with backticks
and replace double-quotes. Drop double-quotes from nested constructions
such as `"@{-1}"`.

Helped-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
---
 Documentation/git-checkout.txt | 162 ++++++++++++++++-----------------
 1 file changed, 81 insertions(+), 81 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 5280d1f9ed..bf90966c95 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-	To prepare for working on <branch>, switch to it by updating
+	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
-	HEAD at the branch. Local modifications to the files in the
+	`HEAD` at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
-	<branch>.
+	`<branch>`.
 +
-If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
+If `<branch>` is not found but there does exist a tracking branch in
+exactly one remote (call it `<remote>`) with a matching name, treat as
 equivalent to
 +
 ------------
@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the
 'origin' remote. See also `checkout.defaultRemote` in
 linkgit:git-config[1].
 +
-You could omit <branch>, in which case the command degenerates to
+You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
@@ -61,7 +61,7 @@ if exists, for the current branch.
 	`--track` without `-b` implies branch creation; see the
 	description of `--track` below.
 +
-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
 is reset. This is the transactional equivalent of
 +
 ------------
@@ -75,25 +75,25 @@ successful.
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of <commit>, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching `HEAD` at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
 	tree will be the state recorded in the commit plus the local
 	modifications.
 +
-When the <commit> argument is a branch name, the `--detach` option can
-be used to detach HEAD at the tip of the branch (`git checkout
-<branch>` would check out that branch without detaching HEAD).
+When the `<commit>` argument is a branch name, the `--detach` option can
+be used to detach `HEAD` at the tip of the branch (`git checkout
+<branch>` would check out that branch without detaching `HEAD`).
 +
-Omitting <branch> detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
 	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the <tree-ish> (most often a
-	commit).  When a <tree-ish> is given, the paths that
-	match the <pathspec> are updated both in the index and in
+	contents in the index or in the `<tree-ish>` (most often a
+	commit).  When a `<tree-ish>` is given, the paths that
+	match the `<pathspec>` are updated both in the index and in
 	the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
@@ -128,7 +128,7 @@ OPTIONS
 -f::
 --force::
 	When switching branches, proceed even if the index or the
-	working tree differs from HEAD.  This is used to throw away
+	working tree differs from `HEAD`.  This is used to throw away
 	local changes.
 +
 When checking out paths from the index, do not fail upon unmerged
@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new_branch>::
-	Create a new branch named <new_branch> and start it at
-	<start_point>; see linkgit:git-branch[1] for details.
+	Create a new branch named `<new_branch>` and start it at
+	`<start_point>`; see linkgit:git-branch[1] for details.
 
 -B <new_branch>::
-	Creates the branch <new_branch> and start it at <start_point>;
-	if it already exists, then reset it to <start_point>. This is
+	Creates the branch `<new_branch>` and start it at `<start_point>`;
+	if it already exists, then reset it to `<start_point>`. This is
 	equivalent to running "git branch" with "-f"; see
 	linkgit:git-branch[1] for details.
 
@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
 the initial part up to the "*".
-This would tell us to use "hack" as the local branch when branching
-off of "origin/hack" (or "remotes/origin/hack", or even
-"refs/remotes/origin/hack").  If the given name has no slash, or the above
+This would tell us to use `hack` as the local branch when branching
+off of `origin/hack` (or `remotes/origin/hack`, or even
+`refs/remotes/origin/hack`).  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with `-b` in such a case.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
-	branch.autoSetupMerge configuration variable is true.
+	`branch.autoSetupMerge` configuration variable is true.
 
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.
 --detach::
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
-	This is the default behavior of "git checkout <commit>" when
-	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	This is the default behavior of `git checkout <commit>` when
+	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
 --orphan <new_branch>::
-	Create a new 'orphan' branch, named <new_branch>, started from
-	<start_point> and switch to it.  The first commit made on this
+	Create a new 'orphan' branch, named `<new_branch>`, started from
+	`<start_point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
-"git checkout <start_point>".  This allows you to start a new history
-that records a set of paths similar to <start_point> by easily running
-"git commit -a" to make the root commit.
+`git checkout <start_point>`.  This allows you to start a new history
+that records a set of paths similar to `<start_point>` by easily running
+`git commit -a` to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
 without exposing its full history. You might want to do this to publish
@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of
 code.
 +
 If you want to start a disconnected history that records a set of paths
-that is totally different from the one of <start_point>, then you should
+that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
-branch by running "git rm -rf ." from the top level of the working tree.
+branch by running `git rm -rf .` from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
-	update only entries matched by <paths> and sparse patterns
-	in $GIT_DIR/info/sparse-checkout. This option ignores
-	the sparse patterns and adds back any files in <paths>.
+	update only entries matched by `<paths>` and sparse patterns
+	in `$GIT_DIR/info/sparse-checkout`. This option ignores
+	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
 --merge::
@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
 
 --conflict=<style>::
-	The same as --merge option above, but changes the way the
+	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.conflictStyle` configuration variable.  Possible values are
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	<tree-ish> (or the index, if unspecified) and the working
+	`<tree-ish>` (or the index, if unspecified) and the working
 	tree.  The chosen hunks are then applied in reverse to the
-	working tree (and if a <tree-ish> was specified, the index).
+	working tree (and if a `<tree-ish>` was specified, the index).
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
@@ -279,13 +279,13 @@ Note that this option uses the no overlay mode by default (see also
 
 --recurse-submodules::
 --no-recurse-submodules::
-	Using --recurse-submodules will update the content of all initialized
+	Using `--recurse-submodules` will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
-	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
 
 --no-guess::
 	Do not attempt to create a branch if a remote tracking branch
@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also
 	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.
+	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
 	branch is checked out. Otherwise, if it refers to a valid
-	commit, your HEAD becomes "detached" and you are no longer on
+	commit, your `HEAD` becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-You can use the `"@{-N}"` syntax to refer to the N-th last
+You can use the `@{-N}` syntax to refer to the N-th last
 branch/commit checked out using "git checkout" operation. You may
-also specify `-` which is synonymous to `"@{-1}"`.
+also specify `-` which is synonymous to `@{-1}`.
 +
-As a special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `A...B` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <start_point>::
 	The name of a commit at which to start the new branch; see
-	linkgit:git-branch[1] for details. Defaults to HEAD.
+	linkgit:git-branch[1] for details. Defaults to `HEAD`.
 
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 DETACHED HEAD
 -------------
-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
 branch refers to a specific commit. Let's look at a repo with three
-commits, one of them tagged, and with branch 'master' checked out:
+commits, one of them tagged, and with branch `master` checked out:
 
 ------------
            HEAD (refers to branch 'master')
@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')
 ------------
 
 When a commit is created in this state, the branch is updated to refer to
-the new commit. Specifically, 'git commit' creates a new commit 'd', whose
-parent is commit 'c', and then updates branch 'master' to refer to new
-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
-to commit 'd':
+the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+parent is commit `c`, and then updates branch `master` to refer to new
+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+to commit `d`:
 
 ------------
 $ edit; git add; git commit
@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 It is sometimes useful to be able to checkout a commit that is not at
 the tip of any named branch, or even to create a new commit that is not
 referenced by a named branch. Let's look at what happens when we
-checkout commit 'b' (here we show two ways this may be done):
+checkout commit `b` (here we show two ways this may be done):
 
 ------------
 $ git checkout v2.0  # or
@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-Notice that regardless of which checkout command we use, HEAD now refers
-directly to commit 'b'. This is known as being in detached HEAD state.
-It means simply that HEAD refers to a specific commit, as opposed to
+Notice that regardless of which checkout command we use, `HEAD` now refers
+directly to commit `b`. This is known as being in detached `HEAD` state.
+It means simply that `HEAD` refers to a specific commit, as opposed to
 referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-There is now a new commit 'e', but it is referenced only by HEAD. We can
+There is now a new commit `e`, but it is referenced only by `HEAD`. We can
 of course add yet another commit in this state:
 
 ------------
@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 In fact, we can perform all the normal Git operations. But, let's look
-at what happens when we then checkout master:
+at what happens when we then checkout `master`:
 
 ------------
 $ git checkout master
@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 It is important to realize that at this point nothing refers to commit
-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
 by the routine Git garbage collection process, unless we create a reference
-before that happens. If we have not yet moved away from commit 'f',
+before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
@@ -442,19 +442,19 @@ $ git branch foo        <2>
 $ git tag foo           <3>
 ------------
 
-<1> creates a new branch 'foo', which refers to commit 'f', and then
-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-    be in detached HEAD state after this command.
+<1> creates a new branch `foo`, which refers to commit `f`, and then
+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+    be in detached `HEAD` state after this command.
 
-<2> similarly creates a new branch 'foo', which refers to commit 'f',
-    but leaves HEAD detached.
+<2> similarly creates a new branch `foo`, which refers to commit `f`,
+    but leaves `HEAD` detached.
 
-<3> creates a new tag 'foo', which refers to commit 'f',
-    leaving HEAD detached.
+<3> creates a new tag `foo`, which refers to commit `f`,
+    leaving `HEAD` detached.
 
-If we have moved away from commit 'f', then we must first recover its object
+If we have moved away from commit `f`, then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
-it. For example, to see the last two commits to which HEAD referred, we
+it. For example, to see the last two commits to which `HEAD` referred, we
 can use either of these commands:
 
 ------------
@@ -465,12 +465,12 @@ $ git log -g -2 HEAD
 ARGUMENT DISAMBIGUATION
 -----------------------
 
-When there is only one argument given and it is not `--` (e.g. "git
-checkout abc"), and when the argument is both a valid `<tree-ish>`
-(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+When there is only one argument given and it is not `--` (e.g. `git
+checkout abc`), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
 or a directory whose name is "abc" exists), Git would usually ask
 you to disambiguate.  Because checking out a branch is so common an
-operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
 in such a situation.  Use `git checkout -- <pathspec>` if you want
 to checkout these paths out of the index.
 
@@ -478,7 +478,7 @@ EXAMPLES
 --------
 
 . The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes hello.c by
+  the `Makefile` to two revisions back, deletes `hello.c` by
   mistake, and gets it back from the index.
 +
 ------------
@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>
 +
 <1> switch branch
 <2> take a file out of another commit
-<3> restore hello.c from the index
+<3> restore `hello.c` from the index
 +
 If you want to check out _all_ C source files out of the index,
 you can say
@@ -519,7 +519,7 @@ $ git checkout -- hello.c
 $ git checkout mytopic
 ------------
 +
-However, your "wrong" branch and correct "mytopic" branch may
+However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (3 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
                           ` (23 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v5 06/26] checkout: advice how to get out of detached HEAD mode
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (4 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
                           ` (22 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Detached HEAD mode is considered dangerous and confusing for newcomers
and we print a big block of warning how to move forward. But we should
also suggest the user the way to get out of it if they get into detached
HEAD by mistake.

While at there, I also suggest how to turn the advice off. This is
another thing I find annoying with advices and should be dealt with in a
more generic way. But that may require some refactoring in advice.c
first.
---
 advice.c                   | 17 +++++++++++++----
 t/t2020-checkout-detach.sh | 12 ++++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/advice.c b/advice.c
index 567209aa79..b224825637 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: checking out '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by performing another checkout.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"\n"
+	"  git checkout -b <new-branch-name>\n"
+	"\n"
+	"Or undo this checkout with:\n"
+	"\n"
+	"  git checkout -\n"
+	"\n"
+	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..743c7248a2 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit three
 	EOF
 
@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit... three
 	EOF
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 07/26] checkout: keep most #include sorted
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (5 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                           ` (21 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The include list becomes very long and frankly a bit unorganized. With
the exception of builtin.h, cache.h or git-compat-util.h which have to
come first, keep the rest sorted.
---
 builtin/checkout.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0e6037b296..f7801db2fb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,30 +1,30 @@
 #define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
-#include "config.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
 #include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
 #include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
 #include "parse-options.h"
 #include "refs.h"
-#include "object-store.h"
-#include "commit.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "resolve-undo.h"
-#include "submodule-config.h"
-#include "submodule.h"
-#include "advice.h"
 
 static int checkout_optimize_new_branch;
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 08/26] checkout: factor out some code in parse_branchname_arg()
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (6 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                           ` (20 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f7801db2fb..1ce0446bc6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 09/26] checkout: make "opts" in cmd_checkout() a pointer
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (7 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                           ` (19 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1ce0446bc6..963f10b05b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (8 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                           ` (18 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 963f10b05b..b6fc1fc69f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -47,6 +47,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -58,6 +60,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 11/26] checkout: split options[] array in three pieces
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (9 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
                           ` (17 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  1 +
 3 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index b6fc1fc69f..cf1d70511e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 2733393546..caaeed896f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 7d83e2971d..9a90c332a5 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+struct option *parse_options_dup(const struct option *a);
 struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 12/26] checkout: split part of it to new command 'switch'
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (10 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                           ` (16 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and restore. The good
old "git checkout" command is still here and will be until all (or most
of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.
---
 .gitignore                         |   1 +
 Documentation/config/advice.txt    |  13 +-
 Documentation/config/branch.txt    |   4 +-
 Documentation/config/checkout.txt  |   9 +-
 Documentation/config/diff.txt      |   3 +-
 Documentation/git-checkout.txt     |   4 +
 Documentation/git-switch.txt (new) | 276 +++++++++++++++++++++++++++++
 Documentation/gitattributes.txt    |   3 +-
 Documentation/githooks.txt         |   8 +-
 Makefile                           |   1 +
 builtin.h                          |   1 +
 builtin/checkout.c                 |  60 +++++--
 command-list.txt                   |   1 +
 git.c                              |   1 +
 14 files changed, 358 insertions(+), 27 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7374587f9d..c687b92b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index c4118fa196..d6872ffa83 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index bf90966c95..c7192bdefe 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -560,6 +560,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..da2cbaf890
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,276 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--no-guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]
+'git switch' [<options>] --orphan <new-branch>
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c`, `-C`,
+automatically from a remote branch of same name (see `--guess`), or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to `HEAD`). The operation is aborted
+however if the operation leads to loss of local changes, unless told
+otherwise with `--discard-changes` or `--merge`.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The starting point for the new branch. Specifying a
+	`<start-point>` allows you to create a branch based on some
+	other point in history than where HEAD currently points. (Or,
+	in the case of `--detach`, allows you to inspect and detach
+	from some other point.)
++
+You can use the `@{-N}` syntax to refer to the N-th last
+branch/commit switched to using "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `@{-1}`.
+This is often used to switch quickly between two branches, or to undo
+a branch switch by mistake.
++
+As a special case, you may use `A...B` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named `<new-branch>` starting at
+	`<start-point>` before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if `<new-branch>` already
+	exists, it will be reset to `<start-point>`. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
++
+`--guess` is the default behavior. Use `--no-guess` to disable it.
+
+-f::
+--force::
+	An alias for `--discard-changes` and `--ignore-in-progress`.
+
+--discard-changes::
+	Proceed even if the index or the working tree differs from
+	`HEAD`. Both the index and working tree are restored to match
+	the switching target. This is used to throw away local
+	changes.
+
+--ignore-in-progress::
+	`git switch` by default refuses when some operation is in
+	progress (e.g. "git rebase", "git am" ...). This option
+	overrides this safety check and allows switching.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+--progress::
+--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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See `--track` in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use `hack` as
+the local branch when branching off of `origin/hack` (or
+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	`branch.autoSetupMerge` configuration variable is true.
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named `<new-branch>`. All
+	tracked files are removed.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
+	is used, the work trees of submodules will not be updated.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name:
+
+------------
+$ git switch -g new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit `HEAD~3` for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-branch[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 9b41f81c06..cd0f9fa507 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 5bf653c111..8ff72f0613 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -406,7 +407,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 148668368b..8e91db73ad 100644
--- a/Makefile
+++ b/Makefile
@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cf1d70511e..dd3c200cb5 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 2014aab6b8..39582cf511 100644
--- a/git.c
+++ b/git.c
@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 13/26] switch: better names for -b and -B
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (11 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
                           ` (15 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout. Move -b/-B
to cmd_checkout() and new -c/-C with the same functionality in
cmd_switch_branch()
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index dd3c200cb5..400a0d5620 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 14/26] switch: add --discard-changes
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (12 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
                           ` (14 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

--discard-changes is a better name than --force for this option since
it's what really happens. --force is turned to an alias for
--discard-changes. But it's meant to be an alias for potentially more
force options in the future.
---
 builtin/checkout.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 400a0d5620..bd94b04303 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int discard_changes;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
-	if (opts->force) {
+	if (opts->discard_changes) {
 		ret = reset_tree(get_commit_tree(new_branch_info->commit),
 				 opts, 1, writeout_error);
 		if (ret)
@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->force && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->force && opts->merge)
 		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
 
+	if (opts->discard_changes && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
 	if (opts->force_detach && opts->new_branch)
 		die(_("'%s' cannot be used with '%s'"),
 		    "--detach", "-b/-B/--orphan");
@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
+	if (opts->force)
+		opts->discard_changes = 1;
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+			 N_("throw away local modifications")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 15/26] switch: remove -l
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (13 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                           ` (13 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index bd94b04303..85fc60f5f2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 16/26] switch: stop accepting pathspec
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (14 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                           ` (12 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 85fc60f5f2..2c4cab6406 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -54,6 +54,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int discard_changes;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 17/26] switch: reject "do nothing" case
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (15 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                           ` (11 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2c4cab6406..73b7373414 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -55,6 +55,7 @@ struct checkout_opts {
 	int no_dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 18/26] switch: only allow explicit detached HEAD
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (16 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                           ` (10 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)
---
 builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 73b7373414..d72e28aabb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -45,6 +45,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
 	return status;
 }
 
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+	struct object_id oid;
+	char *to_free;
+
+	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+		const char *ref = to_free;
+
+		if (skip_prefix(ref, "refs/tags/", &ref))
+			die(_("a branch is expected, got tag '%s'"), ref);
+		if (skip_prefix(ref, "refs/remotes/", &ref))
+			die(_("a branch is expected, got remote branch '%s'"), ref);
+		die(_("a branch is expected, got '%s'"), ref);
+	}
+	if (branch_info->commit)
+		die(_("a branch is expected, got commit '%s'"), branch_info->name);
+	/*
+	 * This case should never happen because we already die() on
+	 * non-commit, but just in case.
+	 */
+	die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("missing branch or commit argument"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die_expecting_a_branch(new_branch_info);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 19/26] switch: add short option for --detach
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (17 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
                           ` (9 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index d72e28aabb..a14133b474 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 20/26] switch: implicit dwim, use --no-guess to disable it
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (18 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                           ` (8 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is already the default in git-checkout. The real change in here is
just minor cleanup. The main excuse is to explain why dwim is kept default.

Contrary to detach mode that is easy to get into and confusing to get
back out. Automatically creating a tracking branch often does not kick
in as often (you would need a branch of the same name on a remote). And
since the branch creation is reported clearly, the user should be able
to undo/delete it if it's unwanted.
---
 Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++-------
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index c7192bdefe..58f18a0842 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	`<branch>`.
 +
 If `<branch>` is not found but there does exist a tracking branch in
-exactly one remote (call it `<remote>`) with a matching name, treat as
-equivalent to
+exactly one remote (call it `<remote>`) with a matching name and
+`--no-guess` is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	`branch.autoSetupMerge` configuration variable is true.
 
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
 	submodule.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --overlay::
 --no-overlay::
 	In the default overlay mode, `git checkout` never
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a14133b474..ee74379038 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,7 +53,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
 		OPT_END()
@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 21/26] switch: no worktree status unless real branch switch happens
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (19 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
                           ` (7 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 134 ++----------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 8 insertions(+), 148 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index d6872ffa83..6b646813ab 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
-
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ee74379038..2aeaac092d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -26,8 +26,6 @@
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -57,11 +55,7 @@ struct checkout_opts {
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	 /*
-	  * Do the merge if this is the initial checkout. We cannot use
-	  * is_cache_unborn() here because the index hasn't been loaded yet
-	  * so cache_nr and timestamp.sec are always zero.
-	  */
-	if (!file_exists(get_index_file()))
-		return 0;
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
 
 	trace2_cmd_mode("branch");
 
@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "diff.ignoresubmodules")) {
 		struct checkout_opts *opts = cb;
 		handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 22/26] switch: reject if some operation is in progress
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (20 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 23/26] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
                           ` (6 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Unless you know what you're doing, switching to another branch to do
something then switching back could be confusing. Worse, you may even
forget that you're in the middle of something. By the time you realize,
you may have done a ton of work and it gets harder to go back.

The new option --ignore-in-progress is added to override this check.
However it's probably just safer to create a separate worktree for
that separate thing you want to work on and leave this worktree
alone (unless of course creating or preparing worktrees are not cheap).

--force is updated to also imply --ignore-in-progress because it is
supposed to be the "just do your things and don't bother me" option.
---
 builtin/checkout.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2aeaac092d..407ed8be94 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -24,6 +24,7 @@
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
+#include "wt-status.h"
 #include "xdiff-interface.h"
 
 static const char * const checkout_usage[] = {
@@ -56,6 +57,7 @@ struct checkout_opts {
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
+	int can_switch_when_in_progress;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !new_branch_info->path)
 		die_expecting_a_branch(new_branch_info);
 
+	if (!opts->can_switch_when_in_progress) {
+		struct wt_status_state state;
+
+		memset(&state, 0, sizeof(state));
+		wt_status_get_state(the_repository, &state, 0);
+
+		if (state.merge_in_progress)
+			die(_("cannot switch branch while merging"));
+		if (state.am_in_progress)
+			die(_("cannot switch branch in the middle of an am session"));
+		if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+			die(_("cannot switch branch while rebasing"));
+		if (state.cherry_pick_in_progress)
+			die(_("cannot switch branch while cherry-picking"));
+		if (state.revert_in_progress)
+			die(_("cannot switch branch while reverting"));
+		if (state.bisect_in_progress)
+			die(_("cannot switch branch while bisecting"));
+	}
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1371,8 +1393,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
-	if (opts->force)
+	if (opts->force) {
 		opts->discard_changes = 1;
+		opts->can_switch_when_in_progress = 1;
+	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1514,6 +1538,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
+	opts.can_switch_when_in_progress = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1539,6 +1564,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
+		OPT_BOOL(0, "ignore-in-progress", &opts.can_switch_when_in_progress,
+			 N_("allow to switch branch when some operation is still in progress")),
 		OPT_END()
 	};
 	int ret;
@@ -1549,6 +1576,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
+	opts.can_switch_when_in_progress = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 23/26] switch: make --orphan switch to an empty tree
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (21 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
                           ` (5 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Switching and creating branches always involves knowing the
<start-point> to begin the new branch from. Sometimes, people want to
create a new branch that does not have any commits yet; --orphan is a
flag to allow that.

--orphan overrides the default of HEAD for <start-point> instead causing
us to start from an empty history with all tracked files removed from
the index and working tree. The use of --orphan is incompatible with
specifying a <start-point>.

A note on the implementation. An alternative is just create a dummy
commit in-core with empty tree and switch to it. But there's a chance
the commit's SHA-1 may end up somewhere permanent like reflog. It's best
to make sure "commit" pointer is NULL to avoid it.
---
 builtin/checkout.c | 39 +++++++++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 407ed8be94..c7f4b5e3c0 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -58,6 +58,7 @@ struct checkout_opts {
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
 	int can_switch_when_in_progress;
+	int orphan_from_empty_tree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -568,15 +569,21 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
 	int ret;
 	struct lock_file lock_file = LOCK_INIT;
+	struct tree *new_tree;
 
 	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(NULL) < 0)
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->commit)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+	} else
+		new_tree = get_commit_tree(new_branch_info->commit);
 	if (opts->discard_changes) {
-		ret = reset_tree(get_commit_tree(new_branch_info->commit),
-				 opts, 1, writeout_error);
+		ret = reset_tree(new_tree, opts, 1, writeout_error);
 		if (ret)
 			return ret;
 	} else {
@@ -614,7 +621,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 					   &old_branch_info->commit->object.oid :
 					   the_hash_algo->empty_tree);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+		parse_tree(new_tree);
+		tree = new_tree;
 		init_tree_desc(&trees[1], tree->buffer, tree->size);
 
 		ret = unpack_trees(2, trees, &topts);
@@ -663,7 +671,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.verbosity = 0;
 			work = write_tree_from_memory(&o);
 
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 1,
 					 writeout_error);
 			if (ret)
@@ -672,13 +680,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.branch1 = new_branch_info->name;
 			o.branch2 = "local";
 			ret = merge_trees(&o,
-					  get_commit_tree(new_branch_info->commit),
+					  new_tree,
 					  work,
 					  get_commit_tree(old_branch_info->commit),
 					  &result);
 			if (ret < 0)
 				exit(128);
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 0,
 					 writeout_error);
 			strbuf_release(&o.obuf);
@@ -696,7 +704,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->discard_changes && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -897,7 +905,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
 	for_each_ref(add_pending_uninteresting_ref, &revs);
-	add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
 
 	if (prepare_revision_walk(&revs))
 		die(_("internal error in revision walk"));
@@ -932,6 +943,14 @@ static int switch_branches(const struct checkout_opts *opts,
 	if (old_branch_info.path)
 		skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
 
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->name)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_branch_info->commit = NULL;
+		new_branch_info->name = "(empty)";
+		do_merge = 1;
+	}
+
 	if (!new_branch_info->name) {
 		new_branch_info->name = "HEAD";
 		new_branch_info->commit = old_branch_info.commit;
@@ -1235,6 +1254,8 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->new_orphan_branch) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+		if (opts->orphan_from_empty_tree && new_branch_info->name)
+			die(_("'%s' cannot take <start-point>"), "--orphan");
 	} else if (opts->force_detach) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
@@ -1539,6 +1560,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
+	opts.orphan_from_empty_tree = 0;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1577,6 +1599,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 	opts.can_switch_when_in_progress = 0;
+	opts.orphan_from_empty_tree = 1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 24/26] t: add tests for switch
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (22 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 23/26] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 25/26] completion: support switch Nguyễn Thái Ngọc Duy
                           ` (4 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

---
 t/t2060-switch.sh (new +x) | 98 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..81e13cf7f0
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	test_must_fail git switch master^{commit} &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	test_must_fail git switch -c temp &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch from empty' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --orphan new-orphan HEAD &&
+	git switch --orphan new-orphan &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit &&
+	git ls-files >tracked-files &&
+	echo orphan.t >expected &&
+	test_cmp expected tracked-files
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --no-guess foo &&
+	git switch foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'switching when something is in progress' '
+	test_when_finished rm -f .git/MERGE_HEAD &&
+	# fake a merge-in-progress
+	cp .git/HEAD .git/MERGE_HEAD &&
+	test_must_fail git switch -d @^ &&
+	git switch --ignore-in-progress -d @^ &&
+	git switch -
+'
+
+test_done
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 25/26] completion: support switch
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (23 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-21 13:16         ` [PATCH v5 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
                           ` (3 subsequent siblings)
  28 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.
---
 contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 976e4a6548..b24bc48276 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -37,7 +37,8 @@
 #   GIT_COMPLETION_CHECKOUT_NO_GUESS
 #
 #     When set to "1", do not include "DWIM" suggestions in git-checkout
-#     completion (e.g., completing "foo" when "origin/foo" exists).
+#     and git-switch completion (e.g., completing "foo" when "origin/foo"
+#     exists).
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -2158,6 +2159,40 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local track_opt="--track" only_local_ref=n
+		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
+			track_opt=''
+		fi
+		# explicit --guess enables DWIM mode regardless of
+		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
+		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
+			track_opt='--track'
+		fi
+		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y -a -z "$track_opt" ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v5 26/26] doc: promote "git switch"
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (24 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 25/26] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:16         ` Nguyễn Thái Ngọc Duy
  2019-03-22  8:00           ` Andrei Rybak
  2019-03-21 13:32         ` [PATCH v5 00/26] Add new command 'switch' Elijah Newren
                           ` (2 subsequent siblings)
  28 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-21 13:16 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...
---
 Documentation/git-branch.txt           | 12 +++---
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 ++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 19 +++++----
 Documentation/giteveryday.txt          | 24 +++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/gitworkflows.txt         |  3 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 56 +++++++++++++-------------
 advice.c                               | 10 ++---
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             | 20 ++++-----
 18 files changed, 101 insertions(+), 101 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0cd87ddeff..1e2d89b174 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -297,7 +297,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 1af85d404f..0a24a5679e 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5629ba4c5d..cb6fc166e2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 132f8e55f6..cbf901efb4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..f880d21dfb 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
+$ git switch -C mybranch master^2
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..ad455f3e39 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch -C pu next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index ca11c7bdaf..abc0dc6bc7 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -301,8 +301,7 @@ topics on 'next':
 .Rewind and rebuild next
 [caption="Recipe: "]
 =====================================
-* `git checkout next`
-* `git reset --hard master`
+* `git switch -C next master`
 * `git merge ai/topic_in_next1`
 * `git merge ai/topic_in_next2`
 * ...
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..94799faa2b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index b224825637..27e39e6514 100644
--- a/advice.c
+++ b/advice.c
@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n"
+	_("Note: switching to '%s'.\n"
 	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n"
+	"state without impacting any branches by switching back to a branch.\n"
 	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"do so (now or later) by using -c with the switch command. Example:\n"
 	"\n"
-	"  git checkout -b <new-branch-name>\n"
+	"  git switch -c <new-branch-name>\n"
 	"\n"
 	"Or undo this checkout with:\n"
 	"\n"
-	"  git checkout -\n"
+	"  git switch -\n"
 	"\n"
 	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
diff --git a/sha1-name.c b/sha1-name.c
index 6dda2c16df..da0518c8e3 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 743c7248a2..da6809b48f 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	Or undo this checkout with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
 	Or undo this checkout with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
-- 
2.21.0.548.gd3c7d92dc2


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v5 00/26] Add new command 'switch'
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (25 preceding siblings ...)
  2019-03-21 13:16         ` [PATCH v5 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-21 13:32         ` Elijah Newren
  2019-03-22  4:26         ` Junio C Hamano
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
  28 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-21 13:32 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Phillip Wood, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On Thu, Mar 21, 2019 at 6:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> This adds a new command 'git-switch' as the half-replacement for
> 'git-checkout'. Jump to 12/26 as the starting point. The other half is
> git-restore, which is dealt with separately.
>
> The diff delta is shrinking nicely. The two main changes are

I only looked over the range-diff this time, but this round looks good
to me.  Thanks for working on this.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH] checkout.txt: note about losing staged changes with --merge
  2019-03-20 13:57                             ` Duy Nguyen
@ 2019-03-21 13:46                               ` Elijah Newren
  0 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-21 13:46 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Junio C Hamano, Phillip Wood, Git Mailing List, Phillip Wood,
	Eric Sunshine, SZEDER Gábor

On Wed, Mar 20, 2019 at 7:58 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Wed, Mar 20, 2019 at 8:53 PM Elijah Newren <newren@gmail.com> wrote:
> > So, I think we do need something (eventually at least).  Would you
> > prefer we dropped this patch from Duy and instead made 'checkout -m'
> > abort when the index is dirty?
>
> I have no problem with this. Still scratching my head wondering if
> t7201-co.sh has a slightly incorrect setup, or aborting is actually
> wrong. You're probably a better person to understand that test case
> ;-)

It doesn't surprise me at all that some testcases would fail with this
change; it's a change of behavior from the previous implementation.
However, taking a look at that testcase, it looks like it's not a
simple change to make it do something similar because there's at least
one other bug that we need to fix first.  I'll dig in...though I
really want to get my
directory-rename-detection-defaults-to-reporting-conflict series
updated and sent out first.  Since Junio seems to be okay with just
taking your doc update for now, hopefully that's not a problem.  :-)

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v5 00/26] Add new command 'switch'
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (26 preceding siblings ...)
  2019-03-21 13:32         ` [PATCH v5 00/26] Add new command 'switch' Elijah Newren
@ 2019-03-22  4:26         ` Junio C Hamano
  2019-03-22  7:30           ` Duy Nguyen
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
  28 siblings, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-03-22  4:26 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: eckhard.s.maass, git, jacob.keller, martin.agren, newren,
	phillip.wood123, rybak.a.v, sunshine, szeder.dev

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This adds a new command 'git-switch' as the half-replacement for
> 'git-checkout'. Jump to 12/26 as the starting point. The other half is
> git-restore, which is dealt with separately.
>
> The diff delta is shrinking nicely. The two main changes are
>
> - '--orphan <new-branch> <initial-tree>' is no longer accepted
> - --force implies --ignore-in-progress
>
> The rest is the usual typo and bug fixes.

And droppage of sign-off?

Other than that, the changes relative to the previous round looked
all reasonable.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v5 00/26] Add new command 'switch'
  2019-03-22  4:26         ` Junio C Hamano
@ 2019-03-22  7:30           ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-22  7:30 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Eckhard Maaß,
	Git Mailing List, Jacob Keller, Martin Ågren, Elijah Newren,
	Phillip Wood, Andrei Rybak, Eric Sunshine, SZEDER Gábor

On Fri, Mar 22, 2019 at 11:26 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
> > This adds a new command 'git-switch' as the half-replacement for
> > 'git-checkout'. Jump to 12/26 as the starting point. The other half is
> > git-restore, which is dealt with separately.
> >
> > The diff delta is shrinking nicely. The two main changes are
> >
> > - '--orphan <new-branch> <initial-tree>' is no longer accepted
> > - --force implies --ignore-in-progress
> >
> > The rest is the usual typo and bug fixes.
>
> And droppage of sign-off?

I think the amount of options I have to give to format-patch starts to
overwhelm me. Can you forge my sign-off? I'll resend otherwise.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v5 26/26] doc: promote "git switch"
  2019-03-21 13:16         ` [PATCH v5 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-22  8:00           ` Andrei Rybak
  2019-03-22  9:12             ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Andrei Rybak @ 2019-03-22  8:00 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, sunshine, szeder.dev

On 3/21/19 2:16 PM, Nguyễn Thái Ngọc Duy wrote:
> ...
>
> diff --git a/advice.c b/advice.c
> index b224825637..27e39e6514 100644
> --- a/advice.c
> +++ b/advice.c
> @@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)
>  void detach_advice(const char *new_name)
>  {
>  	const char *fmt =
> -	_("Note: checking out '%s'.\n"
> +	_("Note: switching to '%s'.\n"
>  	"\n"
>  	"You are in 'detached HEAD' state. You can look around, make experimental\n"
>  	"changes and commit them, and you can discard any commits you make in this\n"
> -	"state without impacting any branches by performing another checkout.\n"
> +	"state without impacting any branches by switching back to a branch.\n"
>  	"\n"
>  	"If you want to create a new branch to retain commits you create, you may\n"
> -	"do so (now or later) by using -b with the checkout command again. Example:\n"
> +	"do so (now or later) by using -c with the switch command. Example:\n"
>  	"\n"
> -	"  git checkout -b <new-branch-name>\n"
> +	"  git switch -c <new-branch-name>\n"
>  	"\n"
>  	"Or undo this checkout with:\n"

With the start of the message being "switching to ..." this part could probably
be also updated to something like "Or undo this switch with" or "Or undo this
switch or checkout with".

>  	"\n"
> -	"  git checkout -\n"
> +	"  git switch -\n"
>  	"\n"
>  	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
>  


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v5 26/26] doc: promote "git switch"
  2019-03-22  8:00           ` Andrei Rybak
@ 2019-03-22  9:12             ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-22  9:12 UTC (permalink / raw)
  To: Andrei Rybak
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Elijah Newren, Phillip Wood, Eric Sunshine,
	SZEDER Gábor

On Fri, Mar 22, 2019 at 3:00 PM Andrei Rybak <rybak.a.v@gmail.com> wrote:
>
> On 3/21/19 2:16 PM, Nguyễn Thái Ngọc Duy wrote:
> > ...
> >
> > diff --git a/advice.c b/advice.c
> > index b224825637..27e39e6514 100644
> > --- a/advice.c
> > +++ b/advice.c
> > @@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)
> >  void detach_advice(const char *new_name)
> >  {
> >       const char *fmt =
> > -     _("Note: checking out '%s'.\n"
> > +     _("Note: switching to '%s'.\n"
> >       "\n"
> >       "You are in 'detached HEAD' state. You can look around, make experimental\n"
> >       "changes and commit them, and you can discard any commits you make in this\n"
> > -     "state without impacting any branches by performing another checkout.\n"
> > +     "state without impacting any branches by switching back to a branch.\n"
> >       "\n"
> >       "If you want to create a new branch to retain commits you create, you may\n"
> > -     "do so (now or later) by using -b with the checkout command again. Example:\n"
> > +     "do so (now or later) by using -c with the switch command. Example:\n"
> >       "\n"
> > -     "  git checkout -b <new-branch-name>\n"
> > +     "  git switch -c <new-branch-name>\n"
> >       "\n"
> >       "Or undo this checkout with:\n"
>
> With the start of the message being "switching to ..." this part could probably
> be also updated to something like "Or undo this switch with" or "Or undo this
> switch or checkout with".

Maybe the neutral "operation"? E.g. "Or undo this operation with:"

>
> >       "\n"
> > -     "  git checkout -\n"
> > +     "  git switch -\n"
> >       "\n"
> >       "Turn off this advice by setting config variable advice.detachedHead to false\n\n");
> >
>


-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-11 11:16       ` Phillip Wood
                           ` (4 preceding siblings ...)
  2019-03-17  6:00         ` [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index Nguyễn Thái Ngọc Duy
@ 2019-03-26 12:50         ` Duy Nguyen
  2019-03-26 15:01           ` Elijah Newren
  5 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-26 12:50 UTC (permalink / raw)
  To: Phillip Wood, Elijah Newren
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > "git checkout" doing too many things is a source of confusion for many
> > users (and it even bites old timers sometimes). To remedy that, the
> > command will be split into two new ones: switch and
> > something-to-checkout-paths.
>
> I think this is a good idea, thanks for working on it. I wonder if it
> would be a good idea to have the new command refuse to checkout a new
> branch if there is a cherry-pick/revert/merge/rebase in progress (with
> an option to override the check) as switching branches in the middle of
> one of those is likely to be confusing to users (if I do it it is
> normally because I've forgotten that I've not run 'git whatever
> --continue').

Guys, I'm sorry for bringing this up again. Apparently I'm not quite
done with 'git switch' yet (not sure if I will ever be).

There is an interesting behavior in git-checkout (and of couse
git-switch). When you do a successful switch, CHERRY_PICK_HEAD,
REVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and
SQUAHS_MSG, if exists, will be removed.

This basically means that if you switch away, any cherry-pick, merge
or revert in progress is destroyed (in the sense of "--quit" not
"--abort" of course). All of this, I believe, involves merge conflicts
so you can't easily switch away unless you allow to destroy unmerged
entries. So it's still quite safe.

However, it leaves me a funny feeling because some "work-in-progress"
operations are destroyed, but some others (bisect, rebase) are not.
This is git-checkout behavior and I will not change that. But do we
want the same behavior in git-switch? Or do we want
no-destroy-in-progress-whatsoever? Or
destroy-all-commands-in-progress?

It may also be a good idea to attempt to describe the behavior we want
in git-switch.txt. I think if the description gets too complicated,
we're heading a wrong way. The current behavior so far could still be
described as "work-in-progress operations related to merge conflicts
are destroyed", or something along that line. But I'm not quite
convinced it's easily understood.

PS. git-reset shares the same behavior, but it's in a different boat,
I think. Or maybe I should scrap/replace that one as well.

PPS. It may get trickier with cherry-pick which can pick a range of
commits now, not just one. But I never used that feature much to know
what I'm talking about.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 12:50         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
@ 2019-03-26 15:01           ` Elijah Newren
  2019-03-26 15:24             ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-26 15:01 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 26, 2019 at 5:50 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > > "git checkout" doing too many things is a source of confusion for many
> > > users (and it even bites old timers sometimes). To remedy that, the
> > > command will be split into two new ones: switch and
> > > something-to-checkout-paths.
> >
> > I think this is a good idea, thanks for working on it. I wonder if it
> > would be a good idea to have the new command refuse to checkout a new
> > branch if there is a cherry-pick/revert/merge/rebase in progress (with
> > an option to override the check) as switching branches in the middle of
> > one of those is likely to be confusing to users (if I do it it is
> > normally because I've forgotten that I've not run 'git whatever
> > --continue').
>
> Guys, I'm sorry for bringing this up again. Apparently I'm not quite
> done with 'git switch' yet (not sure if I will ever be).
>
> There is an interesting behavior in git-checkout (and of couse
> git-switch). When you do a successful switch, CHERRY_PICK_HEAD,
> REVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and
> SQUAHS_MSG, if exists, will be removed.
>
> This basically means that if you switch away, any cherry-pick, merge
> or revert in progress is destroyed (in the sense of "--quit" not
> "--abort" of course). All of this, I believe, involves merge conflicts
> so you can't easily switch away unless you allow to destroy unmerged
> entries. So it's still quite safe.
>
> However, it leaves me a funny feeling because some "work-in-progress"
> operations are destroyed, but some others (bisect, rebase) are not.
> This is git-checkout behavior and I will not change that. But do we
> want the same behavior in git-switch? Or do we want
> no-destroy-in-progress-whatsoever? Or
> destroy-all-commands-in-progress?

I thought we already decided that we'd abort the switch if there was
any operation-in-progress state?  Or are you asking what should we do
if the user explicitly overrides this error with e.g.
--ignore-in-progress?  In that case, I'd say that the reasonable thing
to do would be to leave all the state files alone.  If we make it
clear out the state, we're simply combining uncommon commands for the
user (<operation> --quit + git switch), which seems like a bad UI path
to go down.  Allowing them to switch to some other branch while
keeping all state files is something they can't do with any other
command, and while I hope people wouldn't want to do that much,
switching while keeping state files is something that can't be done
with combining other commands and thus at least makes sense as
something to consider providing.

> It may also be a good idea to attempt to describe the behavior we want
> in git-switch.txt. I think if the description gets too complicated,
> we're heading a wrong way. The current behavior so far could still be
> described as "work-in-progress operations related to merge conflicts
> are destroyed", or something along that line. But I'm not quite
> convinced it's easily understood.
>
> PS. git-reset shares the same behavior, but it's in a different boat,
> I think. Or maybe I should scrap/replace that one as well.

reset has traditionally been the home of
how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick
or revert was 'reset --hard' (or later 'reset --merge'), skipping a
become-empty cherry-pick or rebase is still 'reset', etc.  So it's not
that surprising to me that it clears out state.  As to whether it
still should, we can address that along with...

> PPS. It may get trickier with cherry-pick which can pick a range of
> commits now, not just one. But I never used that feature much to know
> what I'm talking about.

Yeah, cherry-pick and revert and rebase all allow working on several
items.  It seems bad to me to abort/quit such operations without an
explicit '(cherry-pick|rebase|revert) --(abort|quit)'.  Back when
cherry-pick and revert only worked on a single commit, allowing 'reset
--hard' to clear state didn't seem that unreasonable even if it wasn't
the best UI.  So it all went there until people got used to 'rebase
--abort' and realized it was a better way and started spreading
--abort to other commands.  And you could kind of see us overlooking
that someone could cherry-pick --no-commit and then afterwards
checkout --force to put those changes somewhere else but I think it
was even dangerous/weird back then.  Once cherry-pick and revert
commands were extended to work with multiple commits like rebase does,
I just don't think it makes sense to allow reset to completely clear
out state files anymore.  Also, I don't want to check for one or
multiple commits and make them behave differently; I want git to
consistently require an explicit command-specific abort or quit (or
continue) to clear out the state associated with those
possibly-multi-step operations.  Maybe 'reset --hard' can continue
clearing out merge state just by force of habit, though I'd still
rather encourage 'merge --abort'.  But I don't think it makes sense to
let switch, reset, etc. remove multi-step progress state.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 15:01           ` Elijah Newren
@ 2019-03-26 15:24             ` Duy Nguyen
  2019-03-26 15:48               ` Elijah Newren
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-03-26 15:24 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
>
> On Tue, Mar 26, 2019 at 5:50 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:
> > > > "git checkout" doing too many things is a source of confusion for many
> > > > users (and it even bites old timers sometimes). To remedy that, the
> > > > command will be split into two new ones: switch and
> > > > something-to-checkout-paths.
> > >
> > > I think this is a good idea, thanks for working on it. I wonder if it
> > > would be a good idea to have the new command refuse to checkout a new
> > > branch if there is a cherry-pick/revert/merge/rebase in progress (with
> > > an option to override the check) as switching branches in the middle of
> > > one of those is likely to be confusing to users (if I do it it is
> > > normally because I've forgotten that I've not run 'git whatever
> > > --continue').
> >
> > Guys, I'm sorry for bringing this up again. Apparently I'm not quite
> > done with 'git switch' yet (not sure if I will ever be).
> >
> > There is an interesting behavior in git-checkout (and of couse
> > git-switch). When you do a successful switch, CHERRY_PICK_HEAD,
> > REVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and
> > SQUAHS_MSG, if exists, will be removed.
> >
> > This basically means that if you switch away, any cherry-pick, merge
> > or revert in progress is destroyed (in the sense of "--quit" not
> > "--abort" of course). All of this, I believe, involves merge conflicts
> > so you can't easily switch away unless you allow to destroy unmerged
> > entries. So it's still quite safe.
> >
> > However, it leaves me a funny feeling because some "work-in-progress"
> > operations are destroyed, but some others (bisect, rebase) are not.
> > This is git-checkout behavior and I will not change that. But do we
> > want the same behavior in git-switch? Or do we want
> > no-destroy-in-progress-whatsoever? Or
> > destroy-all-commands-in-progress?
>
> I thought we already decided that we'd abort the switch if there was
> any operation-in-progress state?  Or are you asking what should we do
> if the user explicitly overrides this error with e.g.
> --ignore-in-progress?  In that case, I'd say that the reasonable thing
> to do would be to leave all the state files alone.  If we make it
> clear out the state, we're simply combining uncommon commands for the
> user (<operation> --quit + git switch), which seems like a bad UI path
> to go down.  Allowing them to switch to some other branch while
> keeping all state files is something they can't do with any other
> command, and while I hope people wouldn't want to do that much,
> switching while keeping state files is something that can't be done
> with combining other commands and thus at least makes sense as
> something to consider providing.

Yeah.. --ignore-in-process does not necessarily mean aborting
something when you just want to get out to examine some other commit.
And I agree doing nothing seems like the best (or least
confusing/surprising) option.

There will be some funny thing. Like if you commit after switching
away and MERGE_HEAD is there, I think you will be creating a merge
commit.

But I guess if you choose --ignore-in-progress, you're pretty much on
your own. We could improve "git commit" and friends to realize that
the current state is unlikely safe to resume whatever in-progress
operations. But that's separate and I don't want to dig another hole
to bury myself in while there's still 'git restore' to take care of.

Or maybe I'll just suggest "git <verb> --quit" when the switch is
successful with --ignore-in-progress.

Will wait a while for people to comment (and work on that 'git switch
--recurse-submodules', which seems also needs some work) before
resubmitting.

> > It may also be a good idea to attempt to describe the behavior we want
> > in git-switch.txt. I think if the description gets too complicated,
> > we're heading a wrong way. The current behavior so far could still be
> > described as "work-in-progress operations related to merge conflicts
> > are destroyed", or something along that line. But I'm not quite
> > convinced it's easily understood.
> >
> > PS. git-reset shares the same behavior, but it's in a different boat,
> > I think. Or maybe I should scrap/replace that one as well.
>
> reset has traditionally been the home of
> how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick
> or revert was 'reset --hard' (or later 'reset --merge'), skipping a
> become-empty cherry-pick or rebase is still 'reset', etc.  So it's not
> that surprising to me that it clears out state.
> ...

Yeah but it was surprising to me that this is not even mentioned
anywhere in git-reset.txt. You learn by examples basically, or by
experience. But I digress.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 15:24             ` Duy Nguyen
@ 2019-03-26 15:48               ` Elijah Newren
  2019-03-26 15:57                 ` Duy Nguyen
                                   ` (2 more replies)
  0 siblings, 3 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-26 15:48 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:

> Yeah.. --ignore-in-process does not necessarily mean aborting
> something when you just want to get out to examine some other commit.
> And I agree doing nothing seems like the best (or least
> confusing/surprising) option.
>
> There will be some funny thing. Like if you commit after switching
> away and MERGE_HEAD is there, I think you will be creating a merge
> commit.

Yes, and in the middle of a cherry-pick with a range you've added some
commits to one branch and some to another.  In the middle of a revert
you're doing similar.  It sounds like crazytown to me (and maybe we
shouldn't provide the --ignore-in-process flag unless users clamor for
it -- or maybe we should print a big warning whenever people use it
much like we've traditionally done when people checkout a commit
rather than a branch), but it at least makes sense what is happening
and users had to explicitly state they wanted the in-progress state to
just be ignored and switch anyway.

> But I guess if you choose --ignore-in-progress, you're pretty much on
> your own. We could improve "git commit" and friends to realize that
> the current state is unlikely safe to resume whatever in-progress
> operations. But that's separate and I don't want to dig another hole
> to bury myself in while there's still 'git restore' to take care of.
>
> Or maybe I'll just suggest "git <verb> --quit" when the switch is
> successful with --ignore-in-progress.

Combining that with some kind of warning about weird effects of
applying a mid-operation process across branches seems like a good
idea to me if we're going to have that flag.

> > > PS. git-reset shares the same behavior, but it's in a different boat,
> > > I think. Or maybe I should scrap/replace that one as well.
> >
> > reset has traditionally been the home of
> > how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick
> > or revert was 'reset --hard' (or later 'reset --merge'), skipping a
> > become-empty cherry-pick or rebase is still 'reset', etc.  So it's not
> > that surprising to me that it clears out state.
> > ...
>
> Yeah but it was surprising to me that this is not even mentioned
> anywhere in git-reset.txt. You learn by examples basically, or by
> experience. But I digress.

Yeah that is slightly odd -- but that at least provides a small silver
lining: it makes it easier to decide to change it and move all the
mid-operation-state-clearing to other commands.  :-)

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 15:48               ` Elijah Newren
@ 2019-03-26 15:57                 ` Duy Nguyen
  2019-03-27 10:24                 ` Phillip Wood
  2019-04-01  9:29                 ` Junio C Hamano
  2 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-26 15:57 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Tue, Mar 26, 2019 at 10:48 PM Elijah Newren <newren@gmail.com> wrote:
> > > > PS. git-reset shares the same behavior, but it's in a different boat,
> > > > I think. Or maybe I should scrap/replace that one as well.
> > >
> > > reset has traditionally been the home of
> > > how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick
> > > or revert was 'reset --hard' (or later 'reset --merge'), skipping a
> > > become-empty cherry-pick or rebase is still 'reset', etc.  So it's not
> > > that surprising to me that it clears out state.
> > > ...
> >
> > Yeah but it was surprising to me that this is not even mentioned
> > anywhere in git-reset.txt. You learn by examples basically, or by
> > experience. But I digress.
>
> Yeah that is slightly odd -- but that at least provides a small silver
> lining: it makes it easier to decide to change it and move all the
> mid-operation-state-clearing to other commands.  :-)

Don't tempt me. Elsewhere in some discussion with Ævar I wrote it's
better to add "git-ng" instead of going through the deprecation
process to change behavior of current commands, which also means that
you better design git-ng well because you can't just go "oops, i did
it again" and add "git-nng". And I'm slowly realizing that 'switch'
and 'restore' are just "git-ng checkout" in disguise. That already
increases my stress level a bit.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 15:48               ` Elijah Newren
  2019-03-26 15:57                 ` Duy Nguyen
@ 2019-03-27 10:24                 ` Phillip Wood
  2019-03-28 11:04                   ` Duy Nguyen
  2019-04-01  9:29                 ` Junio C Hamano
  2 siblings, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-27 10:24 UTC (permalink / raw)
  To: Elijah Newren, Duy Nguyen
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On 26/03/2019 15:48, Elijah Newren wrote:
> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
> 
>> Yeah.. --ignore-in-process does not necessarily mean aborting
>> something when you just want to get out to examine some other commit.
>> And I agree doing nothing seems like the best (or least
>> confusing/surprising) option.
>>
>> There will be some funny thing. Like if you commit after switching
>> away and MERGE_HEAD is there, I think you will be creating a merge
>> commit.
> 
> Yes, and in the middle of a cherry-pick with a range you've added some
> commits to one branch and some to another.  In the middle of a revert
> you're doing similar.  It sounds like crazytown to me (and maybe we
> shouldn't provide the --ignore-in-process flag unless users clamor for
> it 

I think that could be the way to go for merges and cherry-picks, or
possibly require --discard-changes as well. The only time I use checkout
like this is during a rebase if I want to rewind it - I edit the todo
list with the output of 'git log --pretty="pick %h %s" --reverse' and do
'git checkout' followed by 'git rebase --continue' Though these days I
could add a 'reset' line to the todo list and skip the checkout.

> -- or maybe we should print a big warning whenever people use it
> much like we've traditionally done when people checkout a commit
> rather than a branch), but it at least makes sense what is happening
> and users had to explicitly state they wanted the in-progress state to
> just be ignored and switch anyway.
> 
>> But I guess if you choose --ignore-in-progress, you're pretty much on
>> your own. We could improve "git commit" and friends to realize that
>> the current state is unlikely safe to resume whatever in-progress
>> operations. But that's separate and I don't want to dig another hole
>> to bury myself in while there's still 'git restore' to take care of.
>>
>> Or maybe I'll just suggest "git <verb> --quit" when the switch is
>> successful with --ignore-in-progress.
> 
> Combining that with some kind of warning about weird effects of
> applying a mid-operation process across branches seems like a good
> idea to me if we're going to have that flag.
> 
>>>> PS. git-reset shares the same behavior, but it's in a different boat,
>>>> I think. Or maybe I should scrap/replace that one as well.
>>>
>>> reset has traditionally been the home of
>>> how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick
>>> or revert was 'reset --hard' (or later 'reset --merge'), skipping a
>>> become-empty cherry-pick or rebase is still 'reset', etc.  So it's not
>>> that surprising to me that it clears out state.
>>> ...
>>
>> Yeah but it was surprising to me that this is not even mentioned
>> anywhere in git-reset.txt. You learn by examples basically, or by
>> experience. But I digress.
> 
> Yeah that is slightly odd -- but that at least provides a small silver
> lining: it makes it easier to decide to change it and move all the
> mid-operation-state-clearing to other commands.  :-)

The missing documentation is odd but I think it makes sense. 'reset
--hard' throws away all the changes in the index and worktree so it
would be confusing if MERGE_HEAD was left behind. 'reset --mixed' throws
away the changes in the index and you cannot do a partial commit if
MERGE_HEAD or CHERRY_PICK_HEAD exist so it probably makes sense there as
well (although if you do 'git reset && git commit -a' it wont remember
the merge/cherry-pick). Some of the other reset modes (--soft and --keep
I think) detect on in progress merge and refuse to run, I've got a patch
somewhere that extends that to cherry-picks. I should probably extend it
to handle --merge as well (are there any other modes, I only ever use
--hard/--mixed/--soft as I'm never sure what the others will do)

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-27 10:24                 ` Phillip Wood
@ 2019-03-28 11:04                   ` Duy Nguyen
  2019-03-28 15:05                     ` Elijah Newren
  2019-03-28 16:23                     ` Phillip Wood
  0 siblings, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-28 11:04 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Elijah Newren, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 26/03/2019 15:48, Elijah Newren wrote:
> > On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
> >> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
> >
> >> Yeah.. --ignore-in-process does not necessarily mean aborting
> >> something when you just want to get out to examine some other commit.
> >> And I agree doing nothing seems like the best (or least
> >> confusing/surprising) option.
> >>
> >> There will be some funny thing. Like if you commit after switching
> >> away and MERGE_HEAD is there, I think you will be creating a merge
> >> commit.
> >
> > Yes, and in the middle of a cherry-pick with a range you've added some
> > commits to one branch and some to another.  In the middle of a revert
> > you're doing similar.  It sounds like crazytown to me (and maybe we
> > shouldn't provide the --ignore-in-process flag unless users clamor for
> > it

I missed this part in my last reading. I think if we could safely
switch away and get back to resume, then --ignore-in-process could
still be useful. I sometimes switch to another commit to check out
stuff then back. For interactive rebase with "edit" command for
example, it's quite safe to do so. (yes the other option is "git
worktree add", but that could be a heavy hammer sometimes)

> I think that could be the way to go for merges and cherry-picks, or

Just so we're clear, what is your "the way" to go? to remove
CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
--ignore-in-process is specified? Or to leave MERGE_* and
CHERRY_PICK_HEAD alone and delete other stuff?

> possibly require --discard-changes as well. The only time I use checkout
> like this is during a rebase if I want to rewind it - I edit the todo
> list with the output of 'git log --pretty="pick %h %s" --reverse' and do
> 'git checkout' followed by 'git rebase --continue' Though these days I
> could add a 'reset' line to the todo list and skip the checkout.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-28 11:04                   ` Duy Nguyen
@ 2019-03-28 15:05                     ` Elijah Newren
  2019-03-28 16:23                     ` Phillip Wood
  1 sibling, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-28 15:05 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Thu, Mar 28, 2019 at 4:04 AM Duy Nguyen <pclouds@gmail.com> wrote:
>
> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >
> > On 26/03/2019 15:48, Elijah Newren wrote:
> > > On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
> > >> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
> > >
> > >> Yeah.. --ignore-in-process does not necessarily mean aborting
> > >> something when you just want to get out to examine some other commit.
> > >> And I agree doing nothing seems like the best (or least
> > >> confusing/surprising) option.
> > >>
> > >> There will be some funny thing. Like if you commit after switching
> > >> away and MERGE_HEAD is there, I think you will be creating a merge
> > >> commit.
> > >
> > > Yes, and in the middle of a cherry-pick with a range you've added some
> > > commits to one branch and some to another.  In the middle of a revert
> > > you're doing similar.  It sounds like crazytown to me (and maybe we
> > > shouldn't provide the --ignore-in-process flag unless users clamor for
> > > it
>
> I missed this part in my last reading. I think if we could safely
> switch away and get back to resume, then --ignore-in-process could
> still be useful. I sometimes switch to another commit to check out
> stuff then back. For interactive rebase with "edit" command for
> example, it's quite safe to do so. (yes the other option is "git
> worktree add", but that could be a heavy hammer sometimes)

Ah, switching not for the purpose of continuing the operation
elsewhere, but just to check things out with the intent of coming back
before continuing.  That does make sense.  Please do add a warning
when users do it, though.

> > I think that could be the way to go for merges and cherry-picks, or
>
> Just so we're clear, what is your "the way" to go? to remove
> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
> --ignore-in-process is specified? Or to leave MERGE_* and
> CHERRY_PICK_HEAD alone and delete other stuff?

Personally, I don't think `switch` should modify in-process state of
other operations ever.  Not merge state, not cherry-pick state, not
rebase state, not bisect state, etc.  And default should be for it to
error out if any such state exists.  In-process state should be
updated almost exclusively by `<operation>
--(continue|abort|skip|quit)` (or in bisect's case, the
bad/good/skip/reset commands).  They only reason I put the 'almost'
qualifier in there is that I'm not opposed to the _reset_ command
continuing to do some things with state because of backward
compatibility (and I might begrudgingly admit the same for other
commands if they touch it though I'm tempted to say any command other
than reset and the operation itself that touches in-process state
represents a bug that should be fixed).  However, I very much want to
steer users away from using reset (or other commands) to change state
and guide them to instead use `<operation>
--(continue|abort|skip|quit)`.  I think we make things confusing and
do users a disservice by not having in-process state be a totally
orthogonal thing that is only updated by that process's subcommand.

If people use --ignore-in-process, I think the state should remain and
we should give a big warning to the user about how the operation is
still in progress and likely to give very confusing results and
suggest either switching back to the current commit or running
`<operation> --quit` for them to clear it.

Also, if --ignore-in-process still fails because they need
--discard-changes too, then we could give a really obnoxious warning
about how they should probably consider --quit first if they really
want to switch.

> > possibly require --discard-changes as well. The only time I use checkout
> > like this is during a rebase if I want to rewind it - I edit the todo
> > list with the output of 'git log --pretty="pick %h %s" --reverse' and do
> > 'git checkout' followed by 'git rebase --continue' Though these days I
> > could add a 'reset' line to the todo list and skip the checkout.

I think Phillip's usecase is entirely consistent with the
don't-have-switch-touch-process-state view.


Hope that helps,
Elijah

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-28 11:04                   ` Duy Nguyen
  2019-03-28 15:05                     ` Elijah Newren
@ 2019-03-28 16:23                     ` Phillip Wood
  2019-03-28 17:39                       ` Elijah Newren
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-03-28 16:23 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Elijah Newren, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On 28/03/2019 11:04, Duy Nguyen wrote:
> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> On 26/03/2019 15:48, Elijah Newren wrote:
>>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
>>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
>>>
>>>> Yeah.. --ignore-in-process does not necessarily mean aborting
>>>> something when you just want to get out to examine some other commit.
>>>> And I agree doing nothing seems like the best (or least
>>>> confusing/surprising) option.
>>>>
>>>> There will be some funny thing. Like if you commit after switching
>>>> away and MERGE_HEAD is there, I think you will be creating a merge
>>>> commit.
>>>
>>> Yes, and in the middle of a cherry-pick with a range you've added some
>>> commits to one branch and some to another.  In the middle of a revert
>>> you're doing similar.  It sounds like crazytown to me (and maybe we
>>> shouldn't provide the --ignore-in-process flag unless users clamor for
>>> it
> 
> I missed this part in my last reading. I think if we could safely
> switch away and get back to resume, then --ignore-in-process could
> still be useful.

If we can get back safely then that makes sense, I'm not sure about 
switching while there are conflicts or staged changes though, it feels 
like there's more potential for things to go wrong there.

> I sometimes switch to another commit to check out
> stuff then back. For interactive rebase with "edit" command for
> example, it's quite safe to do so. (yes the other option is "git
> worktree add", but that could be a heavy hammer sometimes) >
>> I think that could be the way to go for merges and cherry-picks, or
> 
> Just so we're clear, what is your "the way" to go? to remove
> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
> --ignore-in-process is specified? Or to leave MERGE_* and
> CHERRY_PICK_HEAD alone and delete other stuff?

I was agreeing with Elijah about dropping --ignore-in-progress unless 
there's a demand for it or at least restricting it so that it requires 
--discard-changes and aborts in-progress merges and single in-progress 
cherry-picks/reverts. (I'm worried about people switching branches when 
cherry-picking more than one commit, though as you say it can make sense 
during a rebase.)

Best Wishes

Phillip

>> possibly require --discard-changes as well. The only time I use checkout
>> like this is during a rebase if I want to rewind it - I edit the todo
>> list with the output of 'git log --pretty="pick %h %s" --reverse' and do
>> 'git checkout' followed by 'git rebase --continue' Though these days I
>> could add a 'reset' line to the todo list and skip the checkout.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-28 16:23                     ` Phillip Wood
@ 2019-03-28 17:39                       ` Elijah Newren
  2019-03-29 11:04                         ` Phillip Wood
  0 siblings, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-28 17:39 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Hi Phillip,

On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 28/03/2019 11:04, Duy Nguyen wrote:
> > On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >> On 26/03/2019 15:48, Elijah Newren wrote:
> >>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
> >>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:

> >>> Yes, and in the middle of a cherry-pick with a range you've added some
> >>> commits to one branch and some to another.  In the middle of a revert
> >>> you're doing similar.  It sounds like crazytown to me (and maybe we
> >>> shouldn't provide the --ignore-in-process flag unless users clamor for
> >>> it
> >
> > I missed this part in my last reading. I think if we could safely
> > switch away and get back to resume, then --ignore-in-process could
> > still be useful.
>
> If we can get back safely then that makes sense, I'm not sure about
> switching while there are conflicts or staged changes though, it feels
> like there's more potential for things to go wrong there.

I really like that way of putting it; I think that makes it much
clearer.  Note, though that staged changes and conflicts could happen
with any of rebase, merge, cherry-pick, or revert, so this problem is
not limited to a subset of those operations.

> > I sometimes switch to another commit to check out
> > stuff then back. For interactive rebase with "edit" command for
> > example, it's quite safe to do so. (yes the other option is "git
> > worktree add", but that could be a heavy hammer sometimes)
>
> >> I think that could be the way to go for merges and cherry-picks, or
> >
> > Just so we're clear, what is your "the way" to go? to remove
> > CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
> > --ignore-in-process is specified? Or to leave MERGE_* and
> > CHERRY_PICK_HEAD alone and delete other stuff?
>
> I was agreeing with Elijah about dropping --ignore-in-progress unless
> there's a demand for it or at least restricting it so that it requires
> --discard-changes and aborts in-progress merges and single in-progress
> cherry-picks/reverts. (I'm worried about people switching branches when
> cherry-picking more than one commit, though as you say it can make sense
> during a rebase.)

I understand the desire to prevent mis-uses, and I agree that if there
are staged changes or conflicts it's really likely things will go
sideways.  But I think we should instead check for those situations
rather than use e.g. rebase vs. merge as a proxy for whether those
problems could be present.  I am especially concerned with the idea of
having something like "git switch --ignore-in-progress
--discard-changes" being used to quit merges or cherry-picks or
reverts or even rebases.  In my opinion, doing so is creating flags to
combine uncommon pairs of git commands (git <operation> --quit + git
switch) in a way that is far less clear.  I think that's a bad route
to go down, and we should keep the commands orthogonal (if I could
start all over, I'd also make reset and checkout and everything else
stop modifying any in-progress state).

Instead, I would either:

  * Drop `--ignore-in-progress` for now.  (Although Duy had a
meaningful usecase)

OR

  * Make `git switch --ignore-in-progress <branch>` leave all process
state in place and switch branches, if we would otherwise be able to
switch branches (i.e. there isn't dirty or conflicted changes in the
way).  BUT, make sure to also:
  * Make '--ignore-in-progress' incompatible with both '-m' and
'--discard-changes'; if folks try to use either of those additional
options with --ignore-in-progress, tell people to use `<operation>
--quit` first.  Do NOT provide an override. (Alternatively, refer to
`<operation> --quit` as the override, since it is).


Elijah

^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v6 00/27] Add new command 'switch'
  2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                           ` (27 preceding siblings ...)
  2019-03-22  4:26         ` Junio C Hamano
@ 2019-03-29 10:38         ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 01/27] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
                             ` (27 more replies)
  28 siblings, 28 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This adds a new command 'git-switch' as the half-replacement for
'git-checkout'. Jump to 13/27 as the starting point. The other half is
git-restore, which is dealt with separately.

The sixth time must be the charm, because the seventh time may be the
curse!

The last big change since v5 is from the discussion about
remove_branch_state() and its relation with --ignore-in-progress. By
popular demand, --ignore-in-progress is gone. The discussion is linked
in 23/27, which is also improved to suggest a way out.

That discussion also leads to a new patch, 07/27, which improves 'git
checkout' and tells the user when merge/revert/cherry-pick is canceled.

The rest is just minor updates here and there. And I made sure the
sign-off is back this time :D

Nguyễn Thái Ngọc Duy (27):
  git-checkout.txt: spell out --no-option
  git-checkout.txt: fix one syntax line
  doc: document --overwrite-ignore
  git-checkout.txt: fix monospace typeset
  t: rename t2014-switch.sh to t2014-checkout-switch.sh
  checkout: advice how to get out of detached HEAD mode
  checkout: inform the user when removing branch state
  checkout: keep most #include sorted
  checkout: factor out some code in parse_branchname_arg()
  checkout: make "opts" in cmd_checkout() a pointer
  checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  checkout: split options[] array in three pieces
  checkout: split part of it to new command 'switch'
  switch: better names for -b and -B
  switch: add --discard-changes
  switch: remove -l
  switch: stop accepting pathspec
  switch: reject "do nothing" case
  switch: only allow explicit detached HEAD
  switch: add short option for --detach
  switch: implicit dwim, use --no-guess to disable it
  switch: no worktree status unless real branch switch happens
  switch: reject if some operation is in progress
  switch: make --orphan switch to an empty tree
  t: add tests for switch
  completion: support switch
  doc: promote "git switch"

 .gitignore                                    |   1 +
 Documentation/config/advice.txt               |  13 +-
 Documentation/config/branch.txt               |   4 +-
 Documentation/config/checkout.txt             |  17 +-
 Documentation/config/diff.txt                 |   3 +-
 Documentation/git-branch.txt                  |  12 +-
 Documentation/git-check-ref-format.txt        |   3 +-
 Documentation/git-checkout.txt                | 221 ++++---
 Documentation/git-format-patch.txt            |   2 +-
 Documentation/git-merge-base.txt              |   2 +-
 Documentation/git-merge.txt                   |   5 +
 Documentation/git-rebase.txt                  |   2 +-
 Documentation/git-remote.txt                  |   2 +-
 Documentation/git-rerere.txt                  |  10 +-
 Documentation/git-reset.txt                   |  20 +-
 Documentation/git-stash.txt                   |   9 +-
 Documentation/git-switch.txt (new)            | 271 ++++++++
 Documentation/gitattributes.txt               |   3 +-
 Documentation/gitcore-tutorial.txt            |  19 +-
 Documentation/giteveryday.txt                 |  24 +-
 Documentation/githooks.txt                    |   8 +-
 Documentation/gittutorial.txt                 |   4 +-
 Documentation/gitworkflows.txt                |   3 +-
 Documentation/revisions.txt                   |   2 +-
 Documentation/user-manual.txt                 |  56 +-
 Makefile                                      |   1 +
 advice.c                                      |  17 +-
 branch.c                                      |  11 +-
 branch.h                                      |   2 +-
 builtin.h                                     |   1 +
 builtin/am.c                                  |   2 +-
 builtin/checkout.c                            | 619 ++++++++++--------
 builtin/rebase.c                              |   4 +-
 builtin/reset.c                               |   2 +-
 builtin/revert.c                              |   2 +-
 command-list.txt                              |   1 +
 contrib/completion/git-completion.bash        |  37 +-
 git.c                                         |   1 +
 parse-options-cb.c                            |  17 +
 parse-options.h                               |   1 +
 sha1-name.c                                   |   2 +-
 t/t1090-sparse-checkout-scope.sh              |  14 -
 ...014-switch.sh => t2014-checkout-switch.sh} |   0
 t/t2020-checkout-detach.sh                    |  28 +-
 t/t2060-switch.sh (new +x)                    |  96 +++
 unpack-trees.c                                |   2 +-
 46 files changed, 1071 insertions(+), 505 deletions(-)
 create mode 100644 Documentation/git-switch.txt
 rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)
 create mode 100755 t/t2060-switch.sh

Range-diff dựa trên v5:
 -:  ---------- >  1:  ddcc6f7684 checkout: inform the user when removing branch state
 1:  0bb2aefb85 =  2:  1d91c3ed35 checkout: keep most #include sorted
 2:  ace82aa26f =  3:  1705d50f4e checkout: factor out some code in parse_branchname_arg()
 3:  6e13efcba8 =  4:  dc07f2fd92 checkout: make "opts" in cmd_checkout() a pointer
 4:  f61a042eb8 =  5:  a8292a7173 checkout: move 'confict_style' and 'dwim_..' to checkout_opts
 5:  972cebc568 =  6:  89b7d4baea checkout: split options[] array in three pieces
 6:  970c727b24 !  7:  0862b680c3 checkout: split part of it to new command 'switch'
    @@ -243,18 +243,14 @@
     +
     +-f::
     +--force::
    -+	An alias for `--discard-changes` and `--ignore-in-progress`.
    ++	An alias for `--discard-changes`.
     +
     +--discard-changes::
     +	Proceed even if the index or the working tree differs from
     +	`HEAD`. Both the index and working tree are restored to match
    -+	the switching target. This is used to throw away local
    -+	changes.
    -+
    -+--ignore-in-progress::
    -+	`git switch` by default refuses when some operation is in
    -+	progress (e.g. "git rebase", "git am" ...). This option
    -+	overrides this safety check and allows switching.
    ++	the switching target. If `--recurse-submodules` is specified,
    ++	submodule content is also restored to match the switching
    ++	target. This is used to throw away local changes.
     +
     +-m::
     +--merge::
    @@ -321,13 +317,12 @@
     +
     +--recurse-submodules::
     +--no-recurse-submodules::
    -+	Using `--recurse-submodules` will update the content of all initialized
    -+	submodules according to the commit recorded in the superproject. If
    -+	local modifications in a submodule would be overwritten the checkout
    -+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
    -+	is used, the work trees of submodules will not be updated.
    -+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
    -+	submodule.
    ++	Using `--recurse-submodules` will update the content of all
    ++	initialized submodules according to the commit recorded in the
    ++	superproject. If nothing (or `--no-recurse-submodules`) is
    ++	used, the work trees of submodules will not be updated. Just
    ++	like linkgit:git-submodule[1], this will detach `HEAD` of the
    ++	submodules.
     +
     +EXAMPLES
     +--------
    @@ -385,7 +380,7 @@
     +name:
     +
     +------------
    -+$ git switch -g new-topic
    ++$ git switch new-topic
     +Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
     +Switched to a new branch 'new-topic'
     +------------
 7:  676f5df0fd =  8:  52da28d384 switch: better names for -b and -B
 8:  4e37c0659e !  9:  471b0c47ac switch: add --discard-changes
    @@ -7,6 +7,10 @@
         --discard-changes. But it's meant to be an alias for potentially more
         force options in the future.
     
    +    Side note. It's not obvious from the patch but --discard-changes also
    +    affects submodules if --recurse-submodules is used. The knob to force
    +    updating submodules is hidden behind unpack-trees.c
    +
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
      +++ b/builtin/checkout.c
 9:  a57208d137 = 10:  bf6d12272c switch: remove -l
10:  3de6f95bf2 = 11:  bec2fb8884 switch: stop accepting pathspec
11:  ad225517cd = 12:  ed0dfccf89 switch: reject "do nothing" case
12:  583cfd5cc4 = 13:  d17eabfb01 switch: only allow explicit detached HEAD
13:  1c5aee658d = 14:  59623757ee switch: add short option for --detach
14:  d942ac52e2 = 15:  04dca884f8 switch: implicit dwim, use --no-guess to disable it
15:  37eb152c0d = 16:  c8ff76def6 switch: no worktree status unless real branch switch happens
16:  50d6768afd ! 17:  34727e7d46 switch: reject if some operation is in progress
    @@ -7,13 +7,20 @@
         forget that you're in the middle of something. By the time you realize,
         you may have done a ton of work and it gets harder to go back.
     
    -    The new option --ignore-in-progress is added to override this check.
    -    However it's probably just safer to create a separate worktree for
    -    that separate thing you want to work on and leave this worktree
    -    alone (unless of course creating or preparing worktrees are not cheap).
    +    A new option --ignore-in-progress was considered but dropped because it
    +    was not exactly clear what should happen. Sometimes you can switch away
    +    and get back safely and resume the operation. Sometimes not. And the
    +    git-checkout behavior is automatically clear merge/revert/cherry-pick,
    +    which makes it a bit even more confusing [1].
     
    -    --force is updated to also imply --ignore-in-progress because it is
    -    supposed to be the "just do your things and don't bother me" option.
    +    We may revisit and add this option in the future. But for now play it
    +    safe and not allow it (you can't even skip this check with --force). The
    +    user is suggested to cancel the operation by themselves (and hopefully
    +    they do consider the consequences, not blindly type the command), or to
    +    create a separate worktree instead of switching. The third option is
    +    the good old "git checkout", but it's not mentioned.
    +
    +    [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com
     
      diff --git a/builtin/checkout.c b/builtin/checkout.c
      --- a/builtin/checkout.c
    @@ -35,44 +42,55 @@
      	const char *new_branch;
      	const char *new_branch_force;
     @@
    - 	    !new_branch_info->path)
    - 		die_expecting_a_branch(new_branch_info);
    + 	die(_("a branch is expected, got '%s'"), branch_info->name);
    + }
      
    -+	if (!opts->can_switch_when_in_progress) {
    -+		struct wt_status_state state;
    ++static void die_if_some_operation_in_progress(void)
    ++{
    ++	struct wt_status_state state;
     +
    -+		memset(&state, 0, sizeof(state));
    -+		wt_status_get_state(the_repository, &state, 0);
    ++	memset(&state, 0, sizeof(state));
    ++	wt_status_get_state(the_repository, &state, 0);
     +
    -+		if (state.merge_in_progress)
    -+			die(_("cannot switch branch while merging"));
    -+		if (state.am_in_progress)
    -+			die(_("cannot switch branch in the middle of an am session"));
    -+		if (state.rebase_interactive_in_progress || state.rebase_in_progress)
    -+			die(_("cannot switch branch while rebasing"));
    -+		if (state.cherry_pick_in_progress)
    -+			die(_("cannot switch branch while cherry-picking"));
    -+		if (state.revert_in_progress)
    -+			die(_("cannot switch branch while reverting"));
    -+		if (state.bisect_in_progress)
    -+			die(_("cannot switch branch while bisecting"));
    -+	}
    ++	if (state.merge_in_progress)
    ++		die(_("cannot switch branch while merging\n"
    ++		      "Consider \"git merge --quit\" "
    ++		      "or \"git worktree add\"."));
    ++	if (state.am_in_progress)
    ++		die(_("cannot switch branch in the middle of an am session\n"
    ++		      "Consider \"git am --quit\" "
    ++		      "or \"git worktree add\"."));
    ++	if (state.rebase_interactive_in_progress || state.rebase_in_progress)
    ++		die(_("cannot switch branch while rebasing\n"
    ++		      "Consider \"git rebase --quit\" "
    ++		      "or \"git worktree add\"."));
    ++	if (state.cherry_pick_in_progress)
    ++		die(_("cannot switch branch while cherry-picking\n"
    ++		      "Consider \"git cherry-pick --quit\" "
    ++		      "or \"git worktree add\"."));
    ++	if (state.revert_in_progress)
    ++		die(_("cannot switch branch while reverting\n"
    ++		      "Consider \"git revert --quit\" "
    ++		      "or \"git worktree add\"."));
    ++	if (state.bisect_in_progress)
    ++		die(_("cannot switch branch while bisecting\n"
    ++		      "Consider \"git bisect reset HEAD\" "
    ++		      "or \"git worktree add\"."));
    ++}
    ++
    + static int checkout_branch(struct checkout_opts *opts,
    + 			   struct branch_info *new_branch_info)
    + {
    +@@
    + 	    !new_branch_info->path)
    + 		die_expecting_a_branch(new_branch_info);
    + 
    ++	if (!opts->can_switch_when_in_progress)
    ++		die_if_some_operation_in_progress();
     +
      	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
      	    !opts->ignore_other_worktrees) {
      		int flag;
    -@@
    - 		opts->merge = 1; /* implied */
    - 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
    - 	}
    --	if (opts->force)
    -+	if (opts->force) {
    - 		opts->discard_changes = 1;
    -+		opts->can_switch_when_in_progress = 1;
    -+	}
    - 
    - 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
    - 		die(_("-b, -B and --orphan are mutually exclusive"));
     @@
      	opts.only_merge_on_switching_branches = 0;
      	opts.accept_pathspec = 1;
    @@ -81,15 +99,6 @@
      
      	options = parse_options_dup(checkout_options);
      	options = add_common_options(&opts, options);
    -@@
    - 			 N_("second guess 'git switch <no-such-branch>'")),
    - 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
    - 			 N_("throw away local modifications")),
    -+		OPT_BOOL(0, "ignore-in-progress", &opts.can_switch_when_in_progress,
    -+			 N_("allow to switch branch when some operation is still in progress")),
    - 		OPT_END()
    - 	};
    - 	int ret;
     @@
      	opts.switch_branch_doing_nothing_is_ok = 0;
      	opts.only_merge_on_switching_branches = 1;
17:  af8bb710c8 = 18:  6e1986f0d4 switch: make --orphan switch to an empty tree
18:  4a293a3d53 ! 19:  414a1783fb t: add tests for switch
    @@ -95,13 +95,11 @@
     +	test_cmp expected actual
     +'
     +
    -+test_expect_success 'switching when something is in progress' '
    ++test_expect_success 'not switching when something is in progress' '
     +	test_when_finished rm -f .git/MERGE_HEAD &&
     +	# fake a merge-in-progress
     +	cp .git/HEAD .git/MERGE_HEAD &&
    -+	test_must_fail git switch -d @^ &&
    -+	git switch --ignore-in-progress -d @^ &&
    -+	git switch -
    ++	test_must_fail git switch -d @^
     +'
     +
     +test_done
19:  b10290b378 = 20:  ac6f5fa9f9 completion: support switch
20:  67c5175e5f ! 21:  5a921aa0a1 doc: promote "git switch"
    @@ -9,6 +9,9 @@
         For these reasons, promote it everywhere possible. This includes
         documentation, suggestions/advice from other commands...
     
    +    The "Checking out files" progress line in unpack-trees.c is also updated
    +    to "Updating files" to be neutral to both git-checkout and git-switch.
    +
      diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
      --- a/Documentation/git-branch.txt
      +++ b/Documentation/git-branch.txt
    @@ -662,7 +665,8 @@
     -	"  git checkout -b <new-branch-name>\n"
     +	"  git switch -c <new-branch-name>\n"
      	"\n"
    - 	"Or undo this checkout with:\n"
    +-	"Or undo this checkout with:\n"
    ++	"Or undo this operation with:\n"
      	"\n"
     -	"  git checkout -\n"
     +	"  git switch -\n"
    @@ -705,7 +709,8 @@
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
      
    - 	Or undo this checkout with:
    +-	Or undo this checkout with:
    ++	Or undo this operation with:
      
     -	  git checkout -
     +	  git switch -
    @@ -731,10 +736,24 @@
     -	  git checkout -b <new-branch-name>
     +	  git switch -c <new-branch-name>
      
    - 	Or undo this checkout with:
    +-	Or undo this checkout with:
    ++	Or undo this operation with:
      
     -	  git checkout -
     +	  git switch -
      
      	Turn off this advice by setting config variable advice.detachedHead to false
      
    +
    + diff --git a/unpack-trees.c b/unpack-trees.c
    + --- a/unpack-trees.c
    + +++ b/unpack-trees.c
    +@@
    + 			total++;
    + 	}
    + 
    +-	return start_delayed_progress(_("Checking out files"), total);
    ++	return start_delayed_progress(_("Updating files"), total);
    + }
    + 
    + static void setup_collided_checkout_detection(struct checkout *state,
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v6 01/27] git-checkout.txt: spell out --no-option
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 02/27] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
                             ` (26 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

It's easier to search for and also less cryptic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index f179b43732..99c8c0dc0f 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,7 +118,8 @@ OPTIONS
 --quiet::
 	Quiet, suppress feedback messages.
 
---[no-]progress::
+--progress::
+--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
@@ -262,7 +263,7 @@ 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.
+`--overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
---[no-]recurse-submodules::
+--recurse-submodules::
+--no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also
 	Do not attempt to create a branch if a remote tracking branch
 	of the same name exists.
 
---[no-]overlay::
+--overlay::
+--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
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 02/27] git-checkout.txt: fix one syntax line
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 01/27] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 03/27] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
                             ` (25 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

<branch> can be omitted in this syntax, and it's actually documented a
few paragraphs down:

  You could omit <branch>, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 99c8c0dc0f..28817cfa41 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
+'git checkout' [<branch>]::
 	To prepare for working on <branch>, switch to it by updating
 	the index and the files in the working tree, and by pointing
 	HEAD at the branch. Local modifications to the files in the
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 03/27] doc: document --overwrite-ignore
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 01/27] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 02/27] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 04/27] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
                             ` (24 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

I added this option in git-checkout and git-merge in c1d7036b6b
(checkout,merge: disallow overwriting ignored files with
--no-overwrite-ignore - 2011-11-27) but did not remember to update
documentation. This completes that commit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 6 ++++++
 Documentation/git-merge.txt    | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 28817cfa41..5280d1f9ed 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort
+	the operation when the new branch contains ignored files.
+
 --recurse-submodules::
 --no-recurse-submodules::
 	Using --recurse-submodules will update the content of all initialized
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4cc86469f3..6a9163d8fe 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -87,6 +87,11 @@ will be appended to the specified message.
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 04/27] git-checkout.txt: fix monospace typeset
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (2 preceding siblings ...)
  2019-03-29 10:38           ` [PATCH v6 03/27] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 05/27] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
                             ` (23 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Add backticks where we have none, replace single quotes with backticks
and replace double-quotes. Drop double-quotes from nested constructions
such as `"@{-1}"`.

Helped-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 162 ++++++++++++++++-----------------
 1 file changed, 81 insertions(+), 81 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 5280d1f9ed..bf90966c95 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-	To prepare for working on <branch>, switch to it by updating
+	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
-	HEAD at the branch. Local modifications to the files in the
+	`HEAD` at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
-	<branch>.
+	`<branch>`.
 +
-If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
+If `<branch>` is not found but there does exist a tracking branch in
+exactly one remote (call it `<remote>`) with a matching name, treat as
 equivalent to
 +
 ------------
@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the
 'origin' remote. See also `checkout.defaultRemote` in
 linkgit:git-config[1].
 +
-You could omit <branch>, in which case the command degenerates to
+You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
@@ -61,7 +61,7 @@ if exists, for the current branch.
 	`--track` without `-b` implies branch creation; see the
 	description of `--track` below.
 +
-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
 is reset. This is the transactional equivalent of
 +
 ------------
@@ -75,25 +75,25 @@ successful.
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of <commit>, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching `HEAD` at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
 	tree will be the state recorded in the commit plus the local
 	modifications.
 +
-When the <commit> argument is a branch name, the `--detach` option can
-be used to detach HEAD at the tip of the branch (`git checkout
-<branch>` would check out that branch without detaching HEAD).
+When the `<commit>` argument is a branch name, the `--detach` option can
+be used to detach `HEAD` at the tip of the branch (`git checkout
+<branch>` would check out that branch without detaching `HEAD`).
 +
-Omitting <branch> detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
 	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the <tree-ish> (most often a
-	commit).  When a <tree-ish> is given, the paths that
-	match the <pathspec> are updated both in the index and in
+	contents in the index or in the `<tree-ish>` (most often a
+	commit).  When a `<tree-ish>` is given, the paths that
+	match the `<pathspec>` are updated both in the index and in
 	the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
@@ -128,7 +128,7 @@ OPTIONS
 -f::
 --force::
 	When switching branches, proceed even if the index or the
-	working tree differs from HEAD.  This is used to throw away
+	working tree differs from `HEAD`.  This is used to throw away
 	local changes.
 +
 When checking out paths from the index, do not fail upon unmerged
@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new_branch>::
-	Create a new branch named <new_branch> and start it at
-	<start_point>; see linkgit:git-branch[1] for details.
+	Create a new branch named `<new_branch>` and start it at
+	`<start_point>`; see linkgit:git-branch[1] for details.
 
 -B <new_branch>::
-	Creates the branch <new_branch> and start it at <start_point>;
-	if it already exists, then reset it to <start_point>. This is
+	Creates the branch `<new_branch>` and start it at `<start_point>`;
+	if it already exists, then reset it to `<start_point>`. This is
 	equivalent to running "git branch" with "-f"; see
 	linkgit:git-branch[1] for details.
 
@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
 the initial part up to the "*".
-This would tell us to use "hack" as the local branch when branching
-off of "origin/hack" (or "remotes/origin/hack", or even
-"refs/remotes/origin/hack").  If the given name has no slash, or the above
+This would tell us to use `hack` as the local branch when branching
+off of `origin/hack` (or `remotes/origin/hack`, or even
+`refs/remotes/origin/hack`).  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with `-b` in such a case.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
-	branch.autoSetupMerge configuration variable is true.
+	`branch.autoSetupMerge` configuration variable is true.
 
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.
 --detach::
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
-	This is the default behavior of "git checkout <commit>" when
-	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	This is the default behavior of `git checkout <commit>` when
+	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
 --orphan <new_branch>::
-	Create a new 'orphan' branch, named <new_branch>, started from
-	<start_point> and switch to it.  The first commit made on this
+	Create a new 'orphan' branch, named `<new_branch>`, started from
+	`<start_point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
-"git checkout <start_point>".  This allows you to start a new history
-that records a set of paths similar to <start_point> by easily running
-"git commit -a" to make the root commit.
+`git checkout <start_point>`.  This allows you to start a new history
+that records a set of paths similar to `<start_point>` by easily running
+`git commit -a` to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
 without exposing its full history. You might want to do this to publish
@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of
 code.
 +
 If you want to start a disconnected history that records a set of paths
-that is totally different from the one of <start_point>, then you should
+that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
-branch by running "git rm -rf ." from the top level of the working tree.
+branch by running `git rm -rf .` from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
-	update only entries matched by <paths> and sparse patterns
-	in $GIT_DIR/info/sparse-checkout. This option ignores
-	the sparse patterns and adds back any files in <paths>.
+	update only entries matched by `<paths>` and sparse patterns
+	in `$GIT_DIR/info/sparse-checkout`. This option ignores
+	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
 --merge::
@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
 
 --conflict=<style>::
-	The same as --merge option above, but changes the way the
+	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.conflictStyle` configuration variable.  Possible values are
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	<tree-ish> (or the index, if unspecified) and the working
+	`<tree-ish>` (or the index, if unspecified) and the working
 	tree.  The chosen hunks are then applied in reverse to the
-	working tree (and if a <tree-ish> was specified, the index).
+	working tree (and if a `<tree-ish>` was specified, the index).
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
@@ -279,13 +279,13 @@ Note that this option uses the no overlay mode by default (see also
 
 --recurse-submodules::
 --no-recurse-submodules::
-	Using --recurse-submodules will update the content of all initialized
+	Using `--recurse-submodules` will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
-	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
 
 --no-guess::
 	Do not attempt to create a branch if a remote tracking branch
@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also
 	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.
+	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
 	branch is checked out. Otherwise, if it refers to a valid
-	commit, your HEAD becomes "detached" and you are no longer on
+	commit, your `HEAD` becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-You can use the `"@{-N}"` syntax to refer to the N-th last
+You can use the `@{-N}` syntax to refer to the N-th last
 branch/commit checked out using "git checkout" operation. You may
-also specify `-` which is synonymous to `"@{-1}"`.
+also specify `-` which is synonymous to `@{-1}`.
 +
-As a special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `A...B` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <start_point>::
 	The name of a commit at which to start the new branch; see
-	linkgit:git-branch[1] for details. Defaults to HEAD.
+	linkgit:git-branch[1] for details. Defaults to `HEAD`.
 
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 DETACHED HEAD
 -------------
-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
 branch refers to a specific commit. Let's look at a repo with three
-commits, one of them tagged, and with branch 'master' checked out:
+commits, one of them tagged, and with branch `master` checked out:
 
 ------------
            HEAD (refers to branch 'master')
@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')
 ------------
 
 When a commit is created in this state, the branch is updated to refer to
-the new commit. Specifically, 'git commit' creates a new commit 'd', whose
-parent is commit 'c', and then updates branch 'master' to refer to new
-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
-to commit 'd':
+the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+parent is commit `c`, and then updates branch `master` to refer to new
+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+to commit `d`:
 
 ------------
 $ edit; git add; git commit
@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 It is sometimes useful to be able to checkout a commit that is not at
 the tip of any named branch, or even to create a new commit that is not
 referenced by a named branch. Let's look at what happens when we
-checkout commit 'b' (here we show two ways this may be done):
+checkout commit `b` (here we show two ways this may be done):
 
 ------------
 $ git checkout v2.0  # or
@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-Notice that regardless of which checkout command we use, HEAD now refers
-directly to commit 'b'. This is known as being in detached HEAD state.
-It means simply that HEAD refers to a specific commit, as opposed to
+Notice that regardless of which checkout command we use, `HEAD` now refers
+directly to commit `b`. This is known as being in detached `HEAD` state.
+It means simply that `HEAD` refers to a specific commit, as opposed to
 referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-There is now a new commit 'e', but it is referenced only by HEAD. We can
+There is now a new commit `e`, but it is referenced only by `HEAD`. We can
 of course add yet another commit in this state:
 
 ------------
@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 In fact, we can perform all the normal Git operations. But, let's look
-at what happens when we then checkout master:
+at what happens when we then checkout `master`:
 
 ------------
 $ git checkout master
@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 It is important to realize that at this point nothing refers to commit
-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
 by the routine Git garbage collection process, unless we create a reference
-before that happens. If we have not yet moved away from commit 'f',
+before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
@@ -442,19 +442,19 @@ $ git branch foo        <2>
 $ git tag foo           <3>
 ------------
 
-<1> creates a new branch 'foo', which refers to commit 'f', and then
-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-    be in detached HEAD state after this command.
+<1> creates a new branch `foo`, which refers to commit `f`, and then
+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+    be in detached `HEAD` state after this command.
 
-<2> similarly creates a new branch 'foo', which refers to commit 'f',
-    but leaves HEAD detached.
+<2> similarly creates a new branch `foo`, which refers to commit `f`,
+    but leaves `HEAD` detached.
 
-<3> creates a new tag 'foo', which refers to commit 'f',
-    leaving HEAD detached.
+<3> creates a new tag `foo`, which refers to commit `f`,
+    leaving `HEAD` detached.
 
-If we have moved away from commit 'f', then we must first recover its object
+If we have moved away from commit `f`, then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
-it. For example, to see the last two commits to which HEAD referred, we
+it. For example, to see the last two commits to which `HEAD` referred, we
 can use either of these commands:
 
 ------------
@@ -465,12 +465,12 @@ $ git log -g -2 HEAD
 ARGUMENT DISAMBIGUATION
 -----------------------
 
-When there is only one argument given and it is not `--` (e.g. "git
-checkout abc"), and when the argument is both a valid `<tree-ish>`
-(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+When there is only one argument given and it is not `--` (e.g. `git
+checkout abc`), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
 or a directory whose name is "abc" exists), Git would usually ask
 you to disambiguate.  Because checking out a branch is so common an
-operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
 in such a situation.  Use `git checkout -- <pathspec>` if you want
 to checkout these paths out of the index.
 
@@ -478,7 +478,7 @@ EXAMPLES
 --------
 
 . The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes hello.c by
+  the `Makefile` to two revisions back, deletes `hello.c` by
   mistake, and gets it back from the index.
 +
 ------------
@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>
 +
 <1> switch branch
 <2> take a file out of another commit
-<3> restore hello.c from the index
+<3> restore `hello.c` from the index
 +
 If you want to check out _all_ C source files out of the index,
 you can say
@@ -519,7 +519,7 @@ $ git checkout -- hello.c
 $ git checkout mytopic
 ------------
 +
-However, your "wrong" branch and correct "mytopic" branch may
+However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 05/27] t: rename t2014-switch.sh to t2014-checkout-switch.sh
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (3 preceding siblings ...)
  2019-03-29 10:38           ` [PATCH v6 04/27] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 06/27] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
                             ` (22 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The old name does not really say that this is about 'checkout -b'. See
49d833dc07 (Revert "checkout branch: prime cache-tree fully" -
2009-05-12) for more information

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply	[flat|nested] 289+ messages in thread

* [PATCH v6 06/27] checkout: advice how to get out of detached HEAD mode
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (4 preceding siblings ...)
  2019-03-29 10:38           ` [PATCH v6 05/27] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:38           ` [PATCH v6 07/27] checkout: inform the user when removing branch state Nguyễn Thái Ngọc Duy
                             ` (21 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Detached HEAD mode is considered dangerous and confusing for newcomers
and we print a big block of warning how to move forward. But we should
also suggest the user the way to get out of it if they get into detached
HEAD by mistake.

While at there, I also suggest how to turn the advice off. This is
another thing I find annoying with advices and should be dealt with in a
more generic way. But that may require some refactoring in advice.c
first.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c                   | 17 +++++++++++++----
 t/t2020-checkout-detach.sh | 12 ++++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/advice.c b/advice.c
index 567209aa79..b224825637 100644
--- a/advice.c
+++ b/advice.c
@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: checking out '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by performing another checkout.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"\n"
+	"  git checkout -b <new-branch-name>\n"
+	"\n"
+	"Or undo this checkout with:\n"
+	"\n"
+	"  git checkout -\n"
+	"\n"
+	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa670625c..743c7248a2 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit three
 	EOF
 
@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	  git checkout -b <new-branch-name>
 
+	Or undo this checkout with:
+
+	  git checkout -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
+
 	HEAD is now at \$commit... three
 	EOF
 
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (5 preceding siblings ...)
  2019-03-29 10:38           ` [PATCH v6 06/27] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:38           ` Nguyễn Thái Ngọc Duy
  2019-07-02  8:06             ` SZEDER Gábor
  2019-03-29 10:39           ` [PATCH v6 08/27] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
                             ` (20 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:38 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

After a successful switch, if a merge, cherry-pick or revert is ongoing,
it is canceled. This behavior has been with us from the very early
beginning, soon after git-merge was created but never actually
documented [1]. It may be a good idea to be transparent and tell the
user if some operation is canceled.

I consider this a better way of telling the user than just adding a
sentence or two in git-checkout.txt, which will be mostly ignored
anyway.

PS. Originally I wanted to print more details like

    warning: cancelling an in-progress merge from <SHA-1>

which may allow some level of undo if the user wants to. But that seems
a lot more work. Perhaps it can be improved later if people still want
that.

[1] ... and I will try not to argue whether it is a sensible behavior.
There is some more discussion here if people are interested:
CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c           | 11 +++++++----
 branch.h           |  2 +-
 builtin/am.c       |  2 +-
 builtin/checkout.c |  2 +-
 builtin/rebase.c   |  4 ++--
 builtin/reset.c    |  2 +-
 builtin/revert.c   |  2 +-
 7 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/branch.c b/branch.c
index 28b81a7e02..8dd5bb9f1c 100644
--- a/branch.c
+++ b/branch.c
@@ -337,11 +337,14 @@ void create_branch(struct repository *r,
 	free(real_ref);
 }
 
-void remove_branch_state(struct repository *r)
+void remove_branch_state(struct repository *r, int verbose)
 {
-	unlink(git_path_cherry_pick_head(r));
-	unlink(git_path_revert_head(r));
-	unlink(git_path_merge_head(r));
+	if (!unlink(git_path_cherry_pick_head(r)) && verbose)
+		warning(_("cancelling a cherry picking in progress"));
+	if (!unlink(git_path_revert_head(r)) && verbose)
+		warning(_("cancelling a revert in progress"));
+	if (!unlink(git_path_merge_head(r)) && verbose)
+		warning(_("cancelling a merge in progress"));
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
 	unlink(git_path_merge_mode(r));
diff --git a/branch.h b/branch.h
index 29c1afa4d0..aed045901e 100644
--- a/branch.h
+++ b/branch.h
@@ -64,7 +64,7 @@ extern int validate_new_branchname(const char *name, struct strbuf *ref, int for
  * Remove information about the state of working on the current
  * branch. (E.g., MERGE_HEAD)
  */
-void remove_branch_state(struct repository *r);
+void remove_branch_state(struct repository *r, int verbose);
 
 /*
  * Configure local branch "local" as downstream to branch "remote"
diff --git a/builtin/am.c b/builtin/am.c
index 4fb107a9d1..99b66508fd 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1957,7 +1957,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
 	if (merge_tree(remote_tree))
 		return -1;
 
-	remove_branch_state(the_repository);
+	remove_branch_state(the_repository, 0);
 
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0e6037b296..f66bd2f56d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -899,7 +899,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 				delete_reflog(old_branch_info->path);
 		}
 	}
-	remove_branch_state(the_repository);
+	remove_branch_state(the_repository, !opts->quiet);
 	strbuf_release(&msg);
 	if (!opts->quiet &&
 	    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 52114cbf0d..646d0f9fb1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1272,7 +1272,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
 			       NULL, NULL) < 0)
 			die(_("could not discard worktree changes"));
-		remove_branch_state(the_repository);
+		remove_branch_state(the_repository, 0);
 		if (read_basic_state(&options))
 			exit(1);
 		goto run_rebase;
@@ -1292,7 +1292,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			       NULL, NULL) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
-		remove_branch_state(the_repository);
+		remove_branch_state(the_repository, 0);
 		ret = finish_rebase(&options);
 		goto cleanup;
 	}
diff --git a/builtin/reset.c b/builtin/reset.c
index 7882829a95..6d9397c844 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -420,7 +420,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
 	if (!pathspec.nr)
-		remove_branch_state(the_repository);
+		remove_branch_state(the_repository, 0);
 
 	return update_ref_status;
 }
diff --git a/builtin/revert.c b/builtin/revert.c
index a47b53ceaf..ebf2789225 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -196,7 +196,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
 	if (cmd == 'q') {
 		int ret = sequencer_remove_state(opts);
 		if (!ret)
-			remove_branch_state(the_repository);
+			remove_branch_state(the_repository, 0);
 		return ret;
 	}
 	if (cmd == 'c')
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 08/27] checkout: keep most #include sorted
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (6 preceding siblings ...)
  2019-03-29 10:38           ` [PATCH v6 07/27] checkout: inform the user when removing branch state Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 09/27] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
                             ` (19 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The include list becomes very long and frankly a bit unorganized. With
the exception of builtin.h, cache.h or git-compat-util.h which have to
come first, keep the rest sorted.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f66bd2f56d..15ee5181eb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,30 +1,30 @@
 #define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
-#include "config.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
 #include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
 #include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
 #include "parse-options.h"
 #include "refs.h"
-#include "object-store.h"
-#include "commit.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "resolve-undo.h"
-#include "submodule-config.h"
-#include "submodule.h"
-#include "advice.h"
 
 static int checkout_optimize_new_branch;
 
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 09/27] checkout: factor out some code in parse_branchname_arg()
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (7 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 08/27] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 10/27] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
                             ` (18 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is in preparation for the new command restore, which also
needs to parse opts->source_tree but does not need all the
disambiguation logic.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 15ee5181eb..897eefe16d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
-
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 10/27] checkout: make "opts" in cmd_checkout() a pointer
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (8 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 09/27] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 11/27] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
                             ` (17 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"opts" will soon be moved out of cmd_checkout(). To keep changes in
that patch smaller, convert "opts" to a pointer and keep the real
thing behind "real_opts".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 115 +++++++++++++++++++++++----------------------
 1 file changed, 58 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 897eefe16d..b6de8401fe 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	char *conflict_style = NULL;
 	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "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_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL(0, "overlay", &opts->overlay_mode, N_("use overlay mode (default)")),
 		OPT_END(),
 	};
 
-	memset(&opts, 0, sizeof(opts));
+	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+	opts->overlay_mode = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
 	if (conflict_style) {
-		opts.merge = 1; /* implied */
+		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	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)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
 	/*
@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (argc) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
+			!opts->patch_mode &&
 			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
 	}
 
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 11/27] checkout: move 'confict_style' and 'dwim_..' to checkout_opts
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (9 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 10/27] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 12/27] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
                             ` (16 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

These local variables are referenced by struct option[]. This struct
will soon be broken down, moved away and we can't rely on local
variables anymore. Move these two to struct checkout_opts in
preparation for that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index b6de8401fe..93fc2a5815 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -47,6 +47,8 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
+	int no_dwim_new_local_branch;
+
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
 	 * should be updated accordingly.
@@ -58,6 +60,7 @@ struct checkout_opts {
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct checkout_opts real_opts;
 	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
+	int dwim_new_local_branch;
 	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
 			 N_("do not second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
+	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
+	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
+	if (opts->conflict_style) {
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 12/27] checkout: split options[] array in three pieces
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (10 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 11/27] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 13/27] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
                             ` (15 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is a preparation step for introducing new commands that do parts
of what checkout does. There will be two new commands, one is about
switching branches, detaching HEAD... one about checking out
paths. These share the a subset of command line options. The rest of
command line options are separate.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------
 parse-options-cb.c | 17 ++++++++++
 parse-options.h    |  1 +
 3 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 93fc2a5815..c15dc91e16 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
-	struct branch_info new_branch_info;
-	int dwim_new_local_branch;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
 		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "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__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_switch_branch_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
 		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
 			      N_("checkout our version for unmerged files"),
 			      2, PARSE_OPT_NONEG),
 		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
 			      N_("checkout their version for unmerged files"),
 			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
-		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-			    "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(),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts real_opts;
+	struct checkout_opts *opts = &real_opts;
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int dwim_new_local_branch;
+	struct option *options = NULL;
 
 	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
+	options = parse_options_dup(options);
+	options = add_common_options(opts, options);
+	options = add_switch_branch_options(opts, options);
+	options = add_checkout_path_options(opts, options);
+
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 2733393546..caaeed896f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index 7d83e2971d..9a90c332a5 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+struct option *parse_options_dup(const struct option *a);
 struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 13/27] checkout: split part of it to new command 'switch'
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (11 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 12/27] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 14/27] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
                             ` (14 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" doing too many things is a source of confusion for many
users (and it even bites old timers sometimes). To remedy that, the
command will be split into two new ones: switch and restore. The good
old "git checkout" command is still here and will be until all (or most
of users) are sick of it.

See the new man page for the final design of switch. The actual
implementation though is still pretty much the same as "git checkout"
and not completely aligned with the man page. Following patches will
adjust their behavior to match the man page.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                         |   1 +
 Documentation/config/advice.txt    |  13 +-
 Documentation/config/branch.txt    |   4 +-
 Documentation/config/checkout.txt  |   9 +-
 Documentation/config/diff.txt      |   3 +-
 Documentation/git-checkout.txt     |   4 +
 Documentation/git-switch.txt (new) | 271 +++++++++++++++++++++++++++++
 Documentation/gitattributes.txt    |   3 +-
 Documentation/githooks.txt         |   8 +-
 Makefile                           |   1 +
 builtin.h                          |   1 +
 builtin/checkout.c                 |  60 +++++--
 command-list.txt                   |   1 +
 git.c                              |   1 +
 14 files changed, 353 insertions(+), 27 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7374587f9d..c687b92b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 88620429ea..239d479506 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -42,7 +42,8 @@ advice.*::
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +63,14 @@ advice.*::
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 019d60ede2..8050466159 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@ branch.autoSetupMerge::
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index c4118fa196..d6872ffa83 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,8 +9,10 @@ checkout.defaultRemote::
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index e48bb987d7..b3b304ee12 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@ diff.external::
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index bf90966c95..c7192bdefe 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -560,6 +560,10 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000000..f15e5bdcf4
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,271 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--no-guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]
+'git switch' [<options>] --orphan <new-branch>
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c`, `-C`,
+automatically from a remote branch of same name (see `--guess`), or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to `HEAD`). The operation is aborted
+however if the operation leads to loss of local changes, unless told
+otherwise with `--discard-changes` or `--merge`.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The starting point for the new branch. Specifying a
+	`<start-point>` allows you to create a branch based on some
+	other point in history than where HEAD currently points. (Or,
+	in the case of `--detach`, allows you to inspect and detach
+	from some other point.)
++
+You can use the `@{-N}` syntax to refer to the N-th last
+branch/commit switched to using "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `@{-1}`.
+This is often used to switch quickly between two branches, or to undo
+a branch switch by mistake.
++
+As a special case, you may use `A...B` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named `<new-branch>` starting at
+	`<start-point>` before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if `<new-branch>` already
+	exists, it will be reset to `<start-point>`. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
++
+`--guess` is the default behavior. Use `--no-guess` to disable it.
+
+-f::
+--force::
+	An alias for `--discard-changes`.
+
+--discard-changes::
+	Proceed even if the index or the working tree differs from
+	`HEAD`. Both the index and working tree are restored to match
+	the switching target. If `--recurse-submodules` is specified,
+	submodule content is also restored to match the switching
+	target. This is used to throw away local changes.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	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).
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+--progress::
+--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`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See `--track` in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use `hack` as
+the local branch when branching off of `origin/hack` (or
+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	`branch.autoSetupMerge` configuration variable is true.
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named `<new-branch>`. All
+	tracked files are removed.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all
+	initialized submodules according to the commit recorded in the
+	superproject. If nothing (or `--no-recurse-submodules`) is
+	used, the work trees of submodules will not be updated. Just
+	like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodules.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name:
+
+------------
+$ git switch new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit `HEAD~3` for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-branch[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 9b41f81c06..cd0f9fa507 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 5bf653c111..8ff72f0613 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -406,7 +407,8 @@ exit with a zero status.
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Makefile b/Makefile
index 148668368b..8e91db73ad 100644
--- a/Makefile
+++ b/Makefile
@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
diff --git a/builtin.h b/builtin.h
index 6538932e99..c64e44450e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index c15dc91e16..4989ca73a3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
 	int dwim_new_local_branch;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
 	opts->overlay_mode = -1;
-	opts->no_dwim_new_local_branch = 0;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = parse_options_dup(options);
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.no_dwim_new_local_branch = 0;
+
+	options = parse_options_dup(options);
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 3a9af104b5..13317f47d4 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -171,6 +171,7 @@ git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/git.c b/git.c
index 2014aab6b8..39582cf511 100644
--- a/git.c
+++ b/git.c
@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 14/27] switch: better names for -b and -B
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (12 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 13/27] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 15/27] switch: add --discard-changes Nguyễn Thái Ngọc Duy
                             ` (13 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The shortcut of these options do not make much sense when used with
switch. And their descriptions are also tied to checkout. Move -b/-B
to cmd_checkout() and new -c/-C with the same functionality in
cmd_switch_branch()

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4989ca73a3..319ba372e3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-static struct option *add_switch_branch_options(struct checkout_opts *opts,
-						struct option *prevopts)
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_STRING('b', NULL, &opts->new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts->new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
-	struct option *options = NULL;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
 	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_END()
+	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 
-	options = parse_options_dup(options);
+	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-	options = add_switch_branch_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
 
 	ret = checkout_main(argc, argv, prefix, &opts,
 			    options, switch_branch_usage);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 15/27] switch: add --discard-changes
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (13 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 14/27] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-04-25 10:02             ` Phillip Wood
  2019-03-29 10:39           ` [PATCH v6 16/27] switch: remove -l Nguyễn Thái Ngọc Duy
                             ` (12 subsequent siblings)
  27 siblings, 1 reply; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

--discard-changes is a better name than --force for this option since
it's what really happens. --force is turned to an alias for
--discard-changes. But it's meant to be an alias for potentially more
force options in the future.

Side note. It's not obvious from the patch but --discard-changes also
affects submodules if --recurse-submodules is used. The knob to force
updating submodules is hidden behind unpack-trees.c

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 319ba372e3..6d0b2ef565 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,6 +53,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int no_dwim_new_local_branch;
+	int discard_changes;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
-	if (opts->force) {
+	if (opts->discard_changes) {
 		ret = reset_tree(get_commit_tree(new_branch_info->commit),
 				 opts, 1, writeout_error);
 		if (ret)
@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->force && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->force && opts->merge)
 		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
 
+	if (opts->discard_changes && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
 	if (opts->force_detach && opts->new_branch)
 		die(_("'%s' cannot be used with '%s'"),
 		    "--detach", "-b/-B/--orphan");
@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		opts->merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
 	}
+	if (opts->force)
+		opts->discard_changes = 1;
 
 	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+			 N_("throw away local modifications")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 16/27] switch: remove -l
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (14 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 15/27] switch: add --discard-changes Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 17/27] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
                             ` (11 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This option is ancient. Nowadays reflog is enabled by default and
automatically created for new branches. Keep it in git-checkout only.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 6d0b2ef565..d63116b686 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL('l', NULL, &opts->new_branch_log, N_("create reflog for new branch")),
 		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 			   N_("create and checkout a new branch")),
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 		OPT_END()
 	};
 	int ret;
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 17/27] switch: stop accepting pathspec
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (15 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 16/27] switch: remove -l Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 18/27] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
                             ` (10 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This command is about switching branch (or creating a new one) and
should not accept pathspec. This helps simplify ambiguation
handling. The other two ("git checkout" and "git restore") of
course do accept pathspec as before.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index d63116b686..0584dc484d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -54,6 +54,7 @@ struct checkout_opts {
 	int overlay_mode;
 	int no_dwim_new_local_branch;
 	int discard_changes;
+	int accept_pathspec;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.accept_pathspec = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 18/27] switch: reject "do nothing" case
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (16 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 17/27] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 19/27] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
                             ` (9 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" can be executed without any arguments. What it does is
not exactly great: it switches from HEAD to HEAD and shows worktree
modification as a side effect.

Make switch reject this case. Just use "git status" if you want
that side effect. For switch, you have to either

- really switch a branch
- (explicitly) detach from the current branch
- create a new branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0584dc484d..e9dfe38d69 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -55,6 +55,7 @@ struct checkout_opts {
 	int no_dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
+	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 
 	options = parse_options_dup(checkout_options);
@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 19/27] switch: only allow explicit detached HEAD
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (17 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 18/27] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 20/27] switch: add short option for --detach Nguyễn Thái Ngọc Duy
                             ` (8 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout <commit>" will checkout the commit in question and
detach HEAD from the current branch. It is naturally a right thing to
do once you get git references. But detached HEAD is a scary concept
to new users because we show a lot of warnings and stuff, and it could
be hard to get out of (until you know better).

To keep switch a bit more friendly to new users, we only allow
entering detached HEAD mode when --detach is given. "git
switch" must take a branch (unless you create a new branch,
then of course switch can take any commit-ish)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index e9dfe38d69..4416b82bfd 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -45,6 +45,7 @@ struct checkout_opts {
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
 	return status;
 }
 
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+	struct object_id oid;
+	char *to_free;
+
+	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+		const char *ref = to_free;
+
+		if (skip_prefix(ref, "refs/tags/", &ref))
+			die(_("a branch is expected, got tag '%s'"), ref);
+		if (skip_prefix(ref, "refs/remotes/", &ref))
+			die(_("a branch is expected, got remote branch '%s'"), ref);
+		die(_("a branch is expected, got '%s'"), ref);
+	}
+	if (branch_info->commit)
+		die(_("a branch is expected, got commit '%s'"), branch_info->name);
+	/*
+	 * This case should never happen because we already die() on
+	 * non-commit, but just in case.
+	 */
+	die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !opts->force_detach)
 		die(_("missing branch or commit argument"));
 
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die_expecting_a_branch(new_branch_info);
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.no_dwim_new_local_branch = 0;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 20/27] switch: add short option for --detach
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (18 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 19/27] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 21/27] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
                             ` (7 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

"git checkout" automatically detaches branches and --detach is not
that useful (--no-detach is more likely). But for "switch", you
may want to use it more often once you're used to detached HEAD. This
of course adds -d to git-checkout but it does not harm (yet?) to do it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4416b82bfd..11308ab80c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(
 	struct checkout_opts *opts, struct option *prevopts)
 {
 	struct option options[] = {
-		OPT_BOOL(0, "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
 		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
 			BRANCH_TRACK_EXPLICIT),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 21/27] switch: implicit dwim, use --no-guess to disable it
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (19 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 20/27] switch: add short option for --detach Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 22/27] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
                             ` (6 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

This is already the default in git-checkout. The real change in here is
just minor cleanup. The main excuse is to explain why dwim is kept default.

Contrary to detach mode that is easy to get into and confusing to get
back out. Automatically creating a tracking branch often does not kick
in as often (you would need a branch of the same name on a remote). And
since the branch creation is reported clearly, the user should be able
to undo/delete it if it's unwanted.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------
 builtin/checkout.c             | 16 +++++++-------
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index c7192bdefe..58f18a0842 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -31,22 +31,13 @@ branch.
 	`<branch>`.
 +
 If `<branch>` is not found but there does exist a tracking branch in
-exactly one remote (call it `<remote>`) with a matching name, treat as
-equivalent to
+exactly one remote (call it `<remote>`) with a matching name and
+`--no-guess` is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.
 	Do not set up "upstream" configuration, even if the
 	`branch.autoSetupMerge` configuration variable is true.
 
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
+
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also
 	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
 	submodule.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
 --overlay::
 --no-overlay::
 	In the default overlay mode, `git checkout` never
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 11308ab80c..ff9b96993d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,7 +53,7 @@ struct checkout_opts {
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	int no_dwim_new_local_branch;
+	int dwim_new_local_branch;
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL(0, "no-guess", &opts->no_dwim_new_local_branch,
-			 N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END()
@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 {
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	int dwim_new_local_branch;
 
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	dwim_new_local_branch = !opts->no_dwim_new_local_branch;
 	if (opts->show_progress < 0) {
 		if (opts->quiet)
 			opts->show_progress = 0;
@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		struct object_id rev;
 		int dwim_ok =
 			!opts->patch_mode &&
-			dwim_new_local_branch &&
+			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and checkout a branch")),
 		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
 		OPT_END()
 	};
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 			   N_("create and switch to a new branch")),
 		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
 			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
 		OPT_END()
@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	int ret;
 
 	memset(&opts, 0, sizeof(opts));
-	opts.no_dwim_new_local_branch = 0;
+	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.implicit_detach = 0;
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 22/27] switch: no worktree status unless real branch switch happens
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (20 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 21/27] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 23/27] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
                             ` (5 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

When we switch from one branch to another, it makes sense to show a
summary of local changes since there could be conflicts, or some files
left modified.... When switch is used solely for creating a new
branch (and "switch" to the same commit) or detaching, we don't really
need to show anything.

"git checkout" does it anyway for historical reasons. But we can start
with a clean slate with switch and don't have to.

This essentially reverts fa655d8411 (checkout: optimize "git checkout
-b <new_branch>" - 2018-08-16) and make it default for switch,
but also for -B and --detach. Users of big repos are encouraged to
move to switch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config/checkout.txt |   8 --
 builtin/checkout.c                | 134 ++----------------------------
 t/t1090-sparse-checkout-scope.sh  |  14 ----
 3 files changed, 8 insertions(+), 148 deletions(-)

diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index d6872ffa83..6b646813ab 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
-
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ff9b96993d..f7967cdb7c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -26,8 +26,6 @@
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
 
-static int checkout_optimize_new_branch;
-
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
@@ -57,11 +55,7 @@ struct checkout_opts {
 	int discard_changes;
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
-
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int only_merge_on_switching_branches;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * 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
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	 /*
-	  * Do the merge if this is the initial checkout. We cannot use
-	  * is_cache_unborn() here because the index hasn't been loaded yet
-	  * so cache_nr and timestamp.sec are always zero.
-	  */
-	if (!file_exists(get_index_file()))
-		return 0;
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
 
 	trace2_cmd_mode("branch");
 
@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "diff.ignoresubmodules")) {
 		struct checkout_opts *opts = cb;
 		handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 
@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.dwim_new_local_branch = 1;
 	opts.accept_pathspec = 0;
 	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 
 	options = parse_options_dup(switch_options);
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc3d3..40cc004326 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 23/27] switch: reject if some operation is in progress
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (21 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 22/27] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 15:47             ` Elijah Newren
  2019-04-25 10:33             ` Phillip Wood
  2019-03-29 10:39           ` [PATCH v6 24/27] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
                             ` (4 subsequent siblings)
  27 siblings, 2 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Unless you know what you're doing, switching to another branch to do
something then switching back could be confusing. Worse, you may even
forget that you're in the middle of something. By the time you realize,
you may have done a ton of work and it gets harder to go back.

A new option --ignore-in-progress was considered but dropped because it
was not exactly clear what should happen. Sometimes you can switch away
and get back safely and resume the operation. Sometimes not. And the
git-checkout behavior is automatically clear merge/revert/cherry-pick,
which makes it a bit even more confusing [1].

We may revisit and add this option in the future. But for now play it
safe and not allow it (you can't even skip this check with --force). The
user is suggested to cancel the operation by themselves (and hopefully
they do consider the consequences, not blindly type the command), or to
create a separate worktree instead of switching. The third option is
the good old "git checkout", but it's not mentioned.

[1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index f7967cdb7c..5f100c1552 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -24,6 +24,7 @@
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
+#include "wt-status.h"
 #include "xdiff-interface.h"
 
 static const char * const checkout_usage[] = {
@@ -56,6 +57,7 @@ struct checkout_opts {
 	int accept_pathspec;
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
+	int can_switch_when_in_progress;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
 	die(_("a branch is expected, got '%s'"), branch_info->name);
 }
 
+static void die_if_some_operation_in_progress(void)
+{
+	struct wt_status_state state;
+
+	memset(&state, 0, sizeof(state));
+	wt_status_get_state(the_repository, &state, 0);
+
+	if (state.merge_in_progress)
+		die(_("cannot switch branch while merging\n"
+		      "Consider \"git merge --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.am_in_progress)
+		die(_("cannot switch branch in the middle of an am session\n"
+		      "Consider \"git am --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+		die(_("cannot switch branch while rebasing\n"
+		      "Consider \"git rebase --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.cherry_pick_in_progress)
+		die(_("cannot switch branch while cherry-picking\n"
+		      "Consider \"git cherry-pick --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.revert_in_progress)
+		die(_("cannot switch branch while reverting\n"
+		      "Consider \"git revert --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.bisect_in_progress)
+		die(_("cannot switch branch while bisecting\n"
+		      "Consider \"git bisect reset HEAD\" "
+		      "or \"git worktree add\"."));
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,
 	    !new_branch_info->path)
 		die_expecting_a_branch(new_branch_info);
 
+	if (!opts->can_switch_when_in_progress)
+		die_if_some_operation_in_progress();
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 0;
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
+	opts.can_switch_when_in_progress = 1;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.switch_branch_doing_nothing_is_ok = 0;
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
+	opts.can_switch_when_in_progress = 0;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 24/27] switch: make --orphan switch to an empty tree
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (22 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 23/27] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 25/27] t: add tests for switch Nguyễn Thái Ngọc Duy
                             ` (3 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Switching and creating branches always involves knowing the
<start-point> to begin the new branch from. Sometimes, people want to
create a new branch that does not have any commits yet; --orphan is a
flag to allow that.

--orphan overrides the default of HEAD for <start-point> instead causing
us to start from an empty history with all tracked files removed from
the index and working tree. The use of --orphan is incompatible with
specifying a <start-point>.

A note on the implementation. An alternative is just create a dummy
commit in-core with empty tree and switch to it. But there's a chance
the commit's SHA-1 may end up somewhere permanent like reflog. It's best
to make sure "commit" pointer is NULL to avoid it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c | 39 +++++++++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5f100c1552..0351735c6e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -58,6 +58,7 @@ struct checkout_opts {
 	int switch_branch_doing_nothing_is_ok;
 	int only_merge_on_switching_branches;
 	int can_switch_when_in_progress;
+	int orphan_from_empty_tree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -568,15 +569,21 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
 	int ret;
 	struct lock_file lock_file = LOCK_INIT;
+	struct tree *new_tree;
 
 	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(NULL) < 0)
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->commit)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+	} else
+		new_tree = get_commit_tree(new_branch_info->commit);
 	if (opts->discard_changes) {
-		ret = reset_tree(get_commit_tree(new_branch_info->commit),
-				 opts, 1, writeout_error);
+		ret = reset_tree(new_tree, opts, 1, writeout_error);
 		if (ret)
 			return ret;
 	} else {
@@ -614,7 +621,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 					   &old_branch_info->commit->object.oid :
 					   the_hash_algo->empty_tree);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+		parse_tree(new_tree);
+		tree = new_tree;
 		init_tree_desc(&trees[1], tree->buffer, tree->size);
 
 		ret = unpack_trees(2, trees, &topts);
@@ -663,7 +671,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.verbosity = 0;
 			work = write_tree_from_memory(&o);
 
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 1,
 					 writeout_error);
 			if (ret)
@@ -672,13 +680,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.branch1 = new_branch_info->name;
 			o.branch2 = "local";
 			ret = merge_trees(&o,
-					  get_commit_tree(new_branch_info->commit),
+					  new_tree,
 					  work,
 					  get_commit_tree(old_branch_info->commit),
 					  &result);
 			if (ret < 0)
 				exit(128);
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 0,
 					 writeout_error);
 			strbuf_release(&o.obuf);
@@ -696,7 +704,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->discard_changes && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -897,7 +905,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
 	for_each_ref(add_pending_uninteresting_ref, &revs);
-	add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
 
 	if (prepare_revision_walk(&revs))
 		die(_("internal error in revision walk"));
@@ -932,6 +943,14 @@ static int switch_branches(const struct checkout_opts *opts,
 	if (old_branch_info.path)
 		skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
 
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->name)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_branch_info->commit = NULL;
+		new_branch_info->name = "(empty)";
+		do_merge = 1;
+	}
+
 	if (!new_branch_info->name) {
 		new_branch_info->name = "HEAD";
 		new_branch_info->commit = old_branch_info.commit;
@@ -1268,6 +1287,8 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->new_orphan_branch) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+		if (opts->orphan_from_empty_tree && new_branch_info->name)
+			die(_("'%s' cannot take <start-point>"), "--orphan");
 	} else if (opts->force_detach) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
@@ -1553,6 +1574,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	opts.accept_pathspec = 1;
 	opts.implicit_detach = 1;
 	opts.can_switch_when_in_progress = 1;
+	opts.orphan_from_empty_tree = 0;
 
 	options = parse_options_dup(checkout_options);
 	options = add_common_options(&opts, options);
@@ -1589,6 +1611,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 	opts.only_merge_on_switching_branches = 1;
 	opts.implicit_detach = 0;
 	opts.can_switch_when_in_progress = 0;
+	opts.orphan_from_empty_tree = 1;
 
 	options = parse_options_dup(switch_options);
 	options = add_common_options(&opts, options);
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 25/27] t: add tests for switch
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (23 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 24/27] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 26/27] completion: support switch Nguyễn Thái Ngọc Duy
                             ` (2 subsequent siblings)
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2060-switch.sh (new +x) | 96 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000000..f9efa29dfb
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	test_must_fail git switch master^{commit} &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	test_must_fail git switch -c temp &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch from empty' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --orphan new-orphan HEAD &&
+	git switch --orphan new-orphan &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit &&
+	git ls-files >tracked-files &&
+	echo orphan.t >expected &&
+	test_cmp expected tracked-files
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --no-guess foo &&
+	git switch foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'not switching when something is in progress' '
+	test_when_finished rm -f .git/MERGE_HEAD &&
+	# fake a merge-in-progress
+	cp .git/HEAD .git/MERGE_HEAD &&
+	test_must_fail git switch -d @^
+'
+
+test_done
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 26/27] completion: support switch
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (24 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 25/27] t: add tests for switch Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 10:39           ` [PATCH v6 27/27] doc: promote "git switch" Nguyễn Thái Ngọc Duy
  2019-03-29 15:58           ` [PATCH v6 00/27] Add new command 'switch' Elijah Newren
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

Completion support for --guess could be made better. If no --detach is
given, we should only provide a list of refs/heads/* and dwim ones,
not the entire ref space. But I still can't penetrate that
__git_refs() function yet.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 976e4a6548..b24bc48276 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -37,7 +37,8 @@
 #   GIT_COMPLETION_CHECKOUT_NO_GUESS
 #
 #     When set to "1", do not include "DWIM" suggestions in git-checkout
-#     completion (e.g., completing "foo" when "origin/foo" exists).
+#     and git-switch completion (e.g., completing "foo" when "origin/foo"
+#     exists).
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -2158,6 +2159,40 @@ _git_status ()
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local track_opt="--track" only_local_ref=n
+		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
+			track_opt=''
+		fi
+		# explicit --guess enables DWIM mode regardless of
+		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
+		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
+			track_opt='--track'
+		fi
+		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		fi
+		if [ $only_local_ref = y -a -z "$track_opt" ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* [PATCH v6 27/27] doc: promote "git switch"
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (25 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 26/27] completion: support switch Nguyễn Thái Ngọc Duy
@ 2019-03-29 10:39           ` Nguyễn Thái Ngọc Duy
  2019-03-29 15:58           ` [PATCH v6 00/27] Add new command 'switch' Elijah Newren
  27 siblings, 0 replies; 289+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2019-03-29 10:39 UTC (permalink / raw)
  To: pclouds
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine, szeder.dev

The new command "git switch" is added to avoid the confusion of
one-command-do-all "git checkout" for new users. They are also helpful
to avoid ambiguation context.

For these reasons, promote it everywhere possible. This includes
documentation, suggestions/advice from other commands...

The "Checking out files" progress line in unpack-trees.c is also updated
to "Updating files" to be neutral to both git-checkout and git-switch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-branch.txt           | 12 +++---
 Documentation/git-check-ref-format.txt |  3 +-
 Documentation/git-format-patch.txt     |  2 +-
 Documentation/git-merge-base.txt       |  2 +-
 Documentation/git-rebase.txt           |  2 +-
 Documentation/git-remote.txt           |  2 +-
 Documentation/git-rerere.txt           | 10 ++---
 Documentation/git-reset.txt            | 20 ++++-----
 Documentation/git-stash.txt            |  9 +++--
 Documentation/gitcore-tutorial.txt     | 19 +++++----
 Documentation/giteveryday.txt          | 24 +++++------
 Documentation/gittutorial.txt          |  4 +-
 Documentation/gitworkflows.txt         |  3 +-
 Documentation/revisions.txt            |  2 +-
 Documentation/user-manual.txt          | 56 +++++++++++++-------------
 advice.c                               | 12 +++---
 sha1-name.c                            |  2 +-
 t/t2020-checkout-detach.sh             | 24 +++++------
 unpack-trees.c                         |  2 +-
 19 files changed, 105 insertions(+), 105 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0cd87ddeff..1e2d89b174 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -297,7 +297,7 @@ Start development from a known tag::
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992585..ee6a4144fb 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 1af85d404f..0a24a5679e 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
+    $ git switch test-apply
     $ git reset --hard
     $ git am a.patch
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f6ed..261d5c1164 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@ instead.
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5629ba4c5d..cb6fc166e2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -17,7 +17,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37fb81..9659abbf8e 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2a58..fe4434ad9d 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 132f8e55f6..cbf901efb4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 7ef8c47911..ebb6282db3 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@ SEE ALSO
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9effcc..f880d21dfb 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@ and check out the state at that time.
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at
 work." commit.
 
 ------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
+$ git switch -C mybranch master^2
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528fc8c..ad455f3e39 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@ following commands.
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -80,7 +80,7 @@ $ git tag v2.43 <2>
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
@@ -90,7 +90,7 @@ $ git commit -a -s <5>
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +216,7 @@ machine into the master branch.
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +274,14 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch -C pu next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31cb6..e6ad6b5f8d 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index ca11c7bdaf..abc0dc6bc7 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -301,8 +301,7 @@ topics on 'next':
 .Rewind and rebuild next
 [caption="Recipe: "]
 =====================================
-* `git checkout next`
-* `git reset --hard master`
+* `git switch -C next master`
 * `git merge ai/topic_in_next1`
 * `git merge ai/topic_in_next2`
 * ...
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 72daa20e76..a1c7a65da6 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890274..94799faa2b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@ a summary of the commands:
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2264,7 @@ tested changes
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -4194,7 +4194,7 @@ start.
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4437,10 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4467,7 @@ $ git branch -r		# list
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/advice.c b/advice.c
index b224825637..24a7741083 100644
--- a/advice.c
+++ b/advice.c
@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n"
+	_("Note: switching to '%s'.\n"
 	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n"
+	"state without impacting any branches by switching back to a branch.\n"
 	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n"
+	"do so (now or later) by using -c with the switch command. Example:\n"
 	"\n"
-	"  git checkout -b <new-branch-name>\n"
+	"  git switch -c <new-branch-name>\n"
 	"\n"
-	"Or undo this checkout with:\n"
+	"Or undo this operation with:\n"
 	"\n"
-	"  git checkout -\n"
+	"  git switch -\n"
 	"\n"
 	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
diff --git a/sha1-name.c b/sha1-name.c
index 6dda2c16df..da0518c8e3 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 743c7248a2..b748db9946 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
-	Or undo this checkout with:
+	Or undo this operation with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
 
-	Or undo this checkout with:
+	Or undo this operation with:
 
-	  git checkout -
+	  git switch -
 
 	Turn off this advice by setting config variable advice.detachedHead to false
 
diff --git a/unpack-trees.c b/unpack-trees.c
index 22c41a3ba8..5ff64a983d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -313,7 +313,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)
 			total++;
 	}
 
-	return start_delayed_progress(_("Checking out files"), total);
+	return start_delayed_progress(_("Updating files"), total);
 }
 
 static void setup_collided_checkout_detection(struct checkout *state,
-- 
2.21.0.479.g47ac719cd3


^ permalink raw reply related	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-28 17:39                       ` Elijah Newren
@ 2019-03-29 11:04                         ` Phillip Wood
  2019-03-29 11:27                           ` Duy Nguyen
  2019-03-29 15:35                           ` Elijah Newren
  0 siblings, 2 replies; 289+ messages in thread
From: Phillip Wood @ 2019-03-29 11:04 UTC (permalink / raw)
  To: Elijah Newren, Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Hi Elijah

On 28/03/2019 17:39, Elijah Newren wrote:
> Hi Phillip,
> 
> On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 28/03/2019 11:04, Duy Nguyen wrote:
>>> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>>> On 26/03/2019 15:48, Elijah Newren wrote:
>>>>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
>>>>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
> 
>>>>> Yes, and in the middle of a cherry-pick with a range you've added some
>>>>> commits to one branch and some to another.  In the middle of a revert
>>>>> you're doing similar.  It sounds like crazytown to me (and maybe we
>>>>> shouldn't provide the --ignore-in-process flag unless users clamor for
>>>>> it
>>>
>>> I missed this part in my last reading. I think if we could safely
>>> switch away and get back to resume, then --ignore-in-process could
>>> still be useful.
>>
>> If we can get back safely then that makes sense, I'm not sure about
>> switching while there are conflicts or staged changes though, it feels
>> like there's more potential for things to go wrong there.
> 
> I really like that way of putting it; I think that makes it much
> clearer.  Note, though that staged changes and conflicts could happen
> with any of rebase, merge, cherry-pick, or revert, so this problem is
> not limited to a subset of those operations.

Indeed

> 
>>> I sometimes switch to another commit to check out
>>> stuff then back. For interactive rebase with "edit" command for
>>> example, it's quite safe to do so. (yes the other option is "git
>>> worktree add", but that could be a heavy hammer sometimes)
>>
>>>> I think that could be the way to go for merges and cherry-picks, or
>>>
>>> Just so we're clear, what is your "the way" to go? to remove
>>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
>>> --ignore-in-process is specified? Or to leave MERGE_* and
>>> CHERRY_PICK_HEAD alone and delete other stuff?
>>
>> I was agreeing with Elijah about dropping --ignore-in-progress unless
>> there's a demand for it or at least restricting it so that it requires
>> --discard-changes and aborts in-progress merges and single in-progress
>> cherry-picks/reverts. (I'm worried about people switching branches when
>> cherry-picking more than one commit, though as you say it can make sense
>> during a rebase.)
> 
> I understand the desire to prevent mis-uses, and I agree that if there
> are staged changes or conflicts it's really likely things will go
> sideways.  But I think we should instead check for those situations
> rather than use e.g. rebase vs. merge as a proxy for whether those
> problems could be present.

When cherry-picking multiple commits if the user commits the conflict 
resolution with 'git commit' then the presence of .git/sequencer is the 
only sign that a cherry-pick is in progress (wt-status.c fails to detect 
this, I've got a fix but no tests yet). rebase can also stop without 
having conflicts or staged changes so I think we need to check for in 
progress commands as well as conflicts (what do we want to do if someone 
tries to switch in the middle of a bisect? - I don't have a strong 
opinion). I agree switch should fail if there are conflicts, but I think 
it is fine to switch with staged or unstaged changes if there isn't a 
merge etc in progress (I quite often start working on something and 
then realize I haven't started a new branch just before I commit). I 
could possibly be convinced that silently switching with staged changes 
is always a bad idea though.

> I am especially concerned with the idea of
> having something like "git switch --ignore-in-progress
> --discard-changes" being used to quit merges or cherry-picks or
> reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git
> switch) in a way that is far less clear.  I think that's a bad route
> to go down, and we should keep the commands orthogonal 

keeping commands orthogonal is certainly clearer, if less convenient - 
lets do it (assuming Duy agrees).

> (if I could
> start all over, I'd also make reset and checkout and everything else
> stop modifying any in-progress state).
> 
> Instead, I would either:
> 
>    * Drop `--ignore-in-progress` for now.  (Although Duy had a
> meaningful usecase)

I think it could be useful during a rebase, I'm not sure about any of 
the other operations though.

> 
> OR
> 
>    * Make `git switch --ignore-in-progress <branch>` leave all process
> state in place and switch branches, if we would otherwise be able to
> switch branches (i.e. there isn't dirty or conflicted changes in the
> way).

I thought we allowed branch switches when there are staged or unstaged 
changes, I don't think that is a problem unless we're in the middle of a 
merge etc. I'm still not sure it's a good idea to switch branches in the 
middle of a multiple cherry-pick, maybe we should print a warning.

>  BUT, make sure to also:
>    * Make '--ignore-in-progress' incompatible with both '-m' and
> '--discard-changes'; if folks try to use either of those additional
> options with --ignore-in-progress, tell people to use `<operation>
> --quit` first.

I think of --discard-changes like --abort. --quit only removes the state 
dir so would pair with -m, as it does not reset the index or working tree.

Overall I think we're more or less in agreement modulo the treatment of 
staged changes when there is no merge etc in progress.

Best Wishes

Phillip

>  Do NOT provide an override. (Alternatively, refer to
> `<operation> --quit` as the override, since it is).
> 
> 
> Elijah
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-29 11:04                         ` Phillip Wood
@ 2019-03-29 11:27                           ` Duy Nguyen
  2019-03-29 15:35                           ` Elijah Newren
  1 sibling, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-03-29 11:27 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Elijah Newren, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

On Fri, Mar 29, 2019 at 6:04 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > I am especially concerned with the idea of
> > having something like "git switch --ignore-in-progress
> > --discard-changes" being used to quit merges or cherry-picks or
> > reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git
> > switch) in a way that is far less clear.  I think that's a bad route
> > to go down, and we should keep the commands orthogonal
>
> keeping commands orthogonal is certainly clearer, if less convenient -
> lets do it (assuming Duy agrees).

I sent out v6 a bit too early I guess. But removing
--ignore-in-progress also makes it orthogonal. You could cancel the
in-progress operation and resume switching, or find another way and
forget about switching.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-29 11:04                         ` Phillip Wood
  2019-03-29 11:27                           ` Duy Nguyen
@ 2019-03-29 15:35                           ` Elijah Newren
  2019-04-25 10:20                             ` Phillip Wood
  1 sibling, 1 reply; 289+ messages in thread
From: Elijah Newren @ 2019-03-29 15:35 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Hi Phillip,

On Fri, Mar 29, 2019 at 4:04 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 28/03/2019 17:39, Elijah Newren wrote:
> > On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >> On 28/03/2019 11:04, Duy Nguyen wrote:

> >>> Just so we're clear, what is your "the way" to go? to remove
> >>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
> >>> --ignore-in-process is specified? Or to leave MERGE_* and
> >>> CHERRY_PICK_HEAD alone and delete other stuff?
> >>
> >> I was agreeing with Elijah about dropping --ignore-in-progress unless
> >> there's a demand for it or at least restricting it so that it requires
> >> --discard-changes and aborts in-progress merges and single in-progress
> >> cherry-picks/reverts. (I'm worried about people switching branches when
> >> cherry-picking more than one commit, though as you say it can make sense
> >> during a rebase.)
> >
> > I understand the desire to prevent mis-uses, and I agree that if there
> > are staged changes or conflicts it's really likely things will go
> > sideways.  But I think we should instead check for those situations
> > rather than use e.g. rebase vs. merge as a proxy for whether those
> > problems could be present.
>
> When cherry-picking multiple commits if the user commits the conflict
> resolution with 'git commit' then the presence of .git/sequencer is the
> only sign that a cherry-pick is in progress (wt-status.c fails to detect
> this, I've got a fix but no tests yet). rebase can also stop without
> having conflicts or staged changes so I think we need to check for in
> progress commands as well as conflicts (what do we want to do if someone

This whole discussion is about "--ignore-in-progress" which implicitly
implies we are checking for in progress commands and choosing whether
to override it.  So I don't understand what you mean by saying we need
to check for it; isn't that a given?

> tries to switch in the middle of a bisect? - I don't have a strong
> opinion). I agree switch should fail if there are conflicts, but I think
> it is fine to switch with staged or unstaged changes if there isn't a
> merge etc in progress (I quite often start working on something and
> then realize I haven't started a new branch just before I commit). I
> could possibly be convinced that silently switching with staged changes
> is always a bad idea though.

I think we might be missing the big picture by trying to discuss
things in terms of in-progress operations or conflicts or staged
changes or unstaged changes.  Allow me to attempt to reframe the
discussion:  We have identified at least one case where allowing the
--ignore-in-progress flag would be unsafe, and we've identified one
where we think it would be safe and useful, thus we need a rule of
thumb for when it is safe to use and when it isn't.  Here's my
attempt:

  --ignore-in-progress is safe enough for usage if we can switch to
another branch and back with no net overall changes to either the
index or the working tree after the two switches.

This rule could allow for the presence of both staged and unstaged
changes (or maybe even conflicts in some alternate world where
checkout/switch didn't necessarily error out on those), depending on
if switch/checkout can operate without touching those particular files
as part of switching.

> > I am especially concerned with the idea of
> > having something like "git switch --ignore-in-progress
> > --discard-changes" being used to quit merges or cherry-picks or
> > reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git
> > switch) in a way that is far less clear.  I think that's a bad route
> > to go down, and we should keep the commands orthogonal
>
> keeping commands orthogonal is certainly clearer, if less convenient -
> lets do it (assuming Duy agrees).

Yaay!

> > (if I could
> > start all over, I'd also make reset and checkout and everything else
> > stop modifying any in-progress state).
> >
> > Instead, I would either:
> >
> >    * Drop `--ignore-in-progress` for now.  (Although Duy had a
> > meaningful usecase)
>
> I think it could be useful during a rebase, I'm not sure about any of
> the other operations though.

I think it could be useful during some rebases, but it should not be
allowed if the user can't switch back to the current commit with no
net overall changes to the index or working tree.

Also, I don't see how rebase is unique here.  Rebase, cherry-pick,
merge, and revert can all stop with conflicts, staged changes, and
unstaged changes.  All of them can also stop without any one of those
(e.g. cherry-pick'ing a commit which has been piecemeal applied
already, merging a branch whose individual changes have already been
cherry-picked and when the user has specified --no-commit, or
reverting a commit whose changes have already been unapplied).  Thus,
I continue to believe that which operation is in progress is
irrelevant.  Either we shouldn't allow switching during any
in-progress operation, or we should determine some other criteria for
when it is safe to allow --ignore-in-progress.  Basing it on the
operation would sometimes allow --ignore-in-progress to be used when
it shouldn't be, and disallow it sometimes when it shouldn't.  I'm a
fan of the rule I mentioned up above ("if we can switch and switch
back with no net changes then it's safe enough to allow")

> >
> > OR
> >
> >    * Make `git switch --ignore-in-progress <branch>` leave all process
> > state in place and switch branches, if we would otherwise be able to
> > switch branches (i.e. there isn't dirty or conflicted changes in the
> > way).
>
> I thought we allowed branch switches when there are staged or unstaged
> changes, I don't think that is a problem unless we're in the middle of a
> merge etc. I'm still not sure it's a good idea to switch branches in the
> middle of a multiple cherry-pick, maybe we should print a warning.

I didn't say to disallow it if there were dirty or conflicted changes,
I said to disallow it if there were dirty or conflicted changes *in
the way*.  We don't allow branch switches when dirty changes would be
overwritten or need to be merged, as that can't easily be reversed.  I
think --ignore-in-progress should only be allowed when it can be
easily reversed to get the user back to the right branch/commit.

This "no net changes" rule also reinforces (or is reinforced by) the
other suggestion I made of having --ignore-in-progress be made
incompatible with both -m and --discard-changes.

But I totally agree that switching branches during the middle of some
operation should print a warning -- not just for cherry-pick, but for
merge or rebase or revert too.  In all cases it'll be important to
tell the user both that they could really mess things up if they try
to resume the operation without switching back, and telling the user
how to get back to where they used to be (in rebase's case, that'd be
"git switch --ignore-in-progress <previous-commit>" while for the
other three it'd be "git switch --ignore-in-progress
<previous-branch>").

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 23/27] switch: reject if some operation is in progress
  2019-03-29 10:39           ` [PATCH v6 23/27] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
@ 2019-03-29 15:47             ` Elijah Newren
  2019-04-25 10:33             ` Phillip Wood
  1 sibling, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-29 15:47 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Phillip Wood, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On Fri, Mar 29, 2019 at 3:42 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> Unless you know what you're doing, switching to another branch to do
> something then switching back could be confusing. Worse, you may even
> forget that you're in the middle of something. By the time you realize,
> you may have done a ton of work and it gets harder to go back.
>
> A new option --ignore-in-progress was considered but dropped because it
> was not exactly clear what should happen. Sometimes you can switch away
> and get back safely and resume the operation. Sometimes not. And the
> git-checkout behavior is automatically clear merge/revert/cherry-pick,
> which makes it a bit even more confusing [1].
>
> We may revisit and add this option in the future. But for now play it
> safe and not allow it (you can't even skip this check with --force). The
> user is suggested to cancel the operation by themselves (and hopefully
> they do consider the consequences, not blindly type the command), or to
> create a separate worktree instead of switching. The third option is
> the good old "git checkout", but it's not mentioned.

I think these safety checks are pretty important for checkout too...

>
> [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 40 insertions(+)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index f7967cdb7c..5f100c1552 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -24,6 +24,7 @@
>  #include "tree.h"
>  #include "tree-walk.h"
>  #include "unpack-trees.h"
> +#include "wt-status.h"
>  #include "xdiff-interface.h"
>
>  static const char * const checkout_usage[] = {
> @@ -56,6 +57,7 @@ struct checkout_opts {
>         int accept_pathspec;
>         int switch_branch_doing_nothing_is_ok;
>         int only_merge_on_switching_branches;
> +       int can_switch_when_in_progress;
>
>         const char *new_branch;
>         const char *new_branch_force;
> @@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
>         die(_("a branch is expected, got '%s'"), branch_info->name);
>  }
>
> +static void die_if_some_operation_in_progress(void)
> +{
> +       struct wt_status_state state;
> +
> +       memset(&state, 0, sizeof(state));
> +       wt_status_get_state(the_repository, &state, 0);
> +
> +       if (state.merge_in_progress)
> +               die(_("cannot switch branch while merging\n"
> +                     "Consider \"git merge --quit\" "
> +                     "or \"git worktree add\"."));
> +       if (state.am_in_progress)
> +               die(_("cannot switch branch in the middle of an am session\n"
> +                     "Consider \"git am --quit\" "
> +                     "or \"git worktree add\"."));
> +       if (state.rebase_interactive_in_progress || state.rebase_in_progress)
> +               die(_("cannot switch branch while rebasing\n"
> +                     "Consider \"git rebase --quit\" "
> +                     "or \"git worktree add\"."));
> +       if (state.cherry_pick_in_progress)
> +               die(_("cannot switch branch while cherry-picking\n"
> +                     "Consider \"git cherry-pick --quit\" "
> +                     "or \"git worktree add\"."));
> +       if (state.revert_in_progress)
> +               die(_("cannot switch branch while reverting\n"
> +                     "Consider \"git revert --quit\" "
> +                     "or \"git worktree add\"."));
> +       if (state.bisect_in_progress)
> +               die(_("cannot switch branch while bisecting\n"
> +                     "Consider \"git bisect reset HEAD\" "
> +                     "or \"git worktree add\"."));
> +}
> +
>  static int checkout_branch(struct checkout_opts *opts,
>                            struct branch_info *new_branch_info)
>  {
> @@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,
>             !new_branch_info->path)
>                 die_expecting_a_branch(new_branch_info);
>
> +       if (!opts->can_switch_when_in_progress)
> +               die_if_some_operation_in_progress();
> +
>         if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
>             !opts->ignore_other_worktrees) {
>                 int flag;
> @@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>         opts.only_merge_on_switching_branches = 0;
>         opts.accept_pathspec = 1;
>         opts.implicit_detach = 1;
> +       opts.can_switch_when_in_progress = 1;

I think this should be 0 too; this check is good for both checkout and
switch.  And if people really do want to use it during in-progress
operations because it is sometimes safe enough, then both operations
deserve some kind of override flag that checks for the appropriate
safety conditions (as we're discussing in the other thread) and then
allows or rejects it.

However, I'm totally fine with proposing another patch after your
series lands to do all of this; this patch is fine as-is for now.

>         options = parse_options_dup(checkout_options);
>         options = add_common_options(&opts, options);
> @@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>         opts.switch_branch_doing_nothing_is_ok = 0;
>         opts.only_merge_on_switching_branches = 1;
>         opts.implicit_detach = 0;
> +       opts.can_switch_when_in_progress = 0;
>
>         options = parse_options_dup(switch_options);
>         options = add_common_options(&opts, options);
> --
> 2.21.0.479.g47ac719cd3

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 00/27] Add new command 'switch'
  2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
                             ` (26 preceding siblings ...)
  2019-03-29 10:39           ` [PATCH v6 27/27] doc: promote "git switch" Nguyễn Thái Ngọc Duy
@ 2019-03-29 15:58           ` Elijah Newren
  27 siblings, 0 replies; 289+ messages in thread
From: Elijah Newren @ 2019-03-29 15:58 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Phillip Wood, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On Fri, Mar 29, 2019 at 3:39 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>
> This adds a new command 'git-switch' as the half-replacement for
> 'git-checkout'. Jump to 13/27 as the starting point. The other half is
> git-restore, which is dealt with separately.
>
> The sixth time must be the charm, because the seventh time may be the
> curse!
>
> The last big change since v5 is from the discussion about
> remove_branch_state() and its relation with --ignore-in-progress. By
> popular demand, --ignore-in-progress is gone. The discussion is linked
> in 23/27, which is also improved to suggest a way out.
>
> That discussion also leads to a new patch, 07/27, which improves 'git
> checkout' and tells the user when merge/revert/cherry-pick is canceled.
>
> The rest is just minor updates here and there. And I made sure the
> sign-off is back this time :D

I looked through the range-diff and it looks good to me.  I think the
safety we added to switch could also be added to checkout, and I think
we're narrowing down on how to safely support --ignore-in-progress in
certain situations, but both of those things could go in after this
series lands...and both might generate more discussion that doesn't
need to derail the rest of this series.

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-26 15:48               ` Elijah Newren
  2019-03-26 15:57                 ` Duy Nguyen
  2019-03-27 10:24                 ` Phillip Wood
@ 2019-04-01  9:29                 ` Junio C Hamano
  2 siblings, 0 replies; 289+ messages in thread
From: Junio C Hamano @ 2019-04-01  9:29 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Duy Nguyen, Phillip Wood, Git Mailing List, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Elijah Newren <newren@gmail.com> writes:

> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:
>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:
>
>> Yeah.. --ignore-in-process does not necessarily mean aborting
>> something when you just want to get out to examine some other commit.
>> And I agree doing nothing seems like the best (or least
>> confusing/surprising) option.
> ...
> Yes, and in the middle of a cherry-pick with a range you've added some
> commits to one branch and some to another.  In the middle of a revert
> you're doing similar.  It sounds like crazytown to me (and maybe we
> shouldn't provide the --ignore-in-process flag unless users clamor for
> it ...

It really sounds like --ignore-in-process that _leaves_ the state
files around which are known to be unrelated to the new commit
pointed out by HEAD is asking for trouble.

We just should tell the users "if you want to go examine something
else during an in-progress operation, do it elsewhere, or cancel the
in-progress operation with 'cmd --abort' (or perhaps 'reset --hard')
which will allow 'switch' to move away".

The thing is, "switch temporarily to another commit, do some
sightseeing and then switch back to the original commit--we did not
touch the in-progress state files while doing so, so we ought to
come back to exactly the same state to be able to continue" is
brittle.  Temporarily switching to another commit or branch would
need to touch the index (in some irrevocable way) and with future
versions of Git, underlying machineries (like UNDO data) other than
the index as well.  What the user would do after (temporarily)
switching out of the in-process state (e.g. controlled by the
sequencer) may be destructive (e.g. "switch away temporarily, forget
that you've switched away and try to run cherry-pick or rebase").


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 15/27] switch: add --discard-changes
  2019-03-29 10:39           ` [PATCH v6 15/27] switch: add --discard-changes Nguyễn Thái Ngọc Duy
@ 2019-04-25 10:02             ` Phillip Wood
  2019-04-25 10:12               ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-04-25 10:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, rybak.a.v, sunshine, szeder.dev

On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
> --discard-changes is a better name than --force for this option since
> it's what really happens.

I didn't realize when I suggested the name that --force overwrites
untracked files as well as discarding changes from tracked files. I
think we should document that. It would be nice if read-tree --reset -u
took an optional argument so read-tree --reset=tracked -u would not
overwrite untracked files. Then we could have --discard-changes just
discard the changes and not overwrite untracked files. I had a quick
look at unpack trees and it looks like a fairly straight forward change
(famous last words) - perhaps I'll have a go at it next week.

Best Wishes

Phillip

> --force is turned to an alias for
> --discard-changes. But it's meant to be an alias for potentially more
> force options in the future.
> 
> Side note. It's not obvious from the patch but --discard-changes also
> affects submodules if --recurse-submodules is used. The knob to force
> updating submodules is hidden behind unpack-trees.c
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 319ba372e3..6d0b2ef565 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -53,6 +53,7 @@ struct checkout_opts {
>  	int count_checkout_paths;
>  	int overlay_mode;
>  	int no_dwim_new_local_branch;
> +	int discard_changes;
>  
>  	/*
>  	 * If new checkout options are added, skip_merge_working_tree
> @@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
>  		return error(_("index file corrupt"));
>  
>  	resolve_undo_clear();
> -	if (opts->force) {
> +	if (opts->discard_changes) {
>  		ret = reset_tree(get_commit_tree(new_branch_info->commit),
>  				 opts, 1, writeout_error);
>  		if (ret)
> @@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
>  	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
>  		die(_("unable to write new index file"));
>  
> -	if (!opts->force && !opts->quiet)
> +	if (!opts->discard_changes && !opts->quiet)
>  		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
>  
>  	return 0;
> @@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,
>  	if (opts->force && opts->merge)
>  		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
>  
> +	if (opts->discard_changes && opts->merge)
> +		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
> +
>  	if (opts->force_detach && opts->new_branch)
>  		die(_("'%s' cannot be used with '%s'"),
>  		    "--detach", "-b/-B/--orphan");
> @@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
>  		opts->merge = 1; /* implied */
>  		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
>  	}
> +	if (opts->force)
> +		opts->discard_changes = 1;
>  
>  	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
>  		die(_("-b, -B and --orphan are mutually exclusive"));
> @@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>  			   N_("create and switch to a new branch")),
>  		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
>  			   N_("create/reset and switch to a branch")),
> +		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
> +			 N_("throw away local modifications")),
>  		OPT_END()
>  	};
>  	int ret;
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 15/27] switch: add --discard-changes
  2019-04-25 10:02             ` Phillip Wood
@ 2019-04-25 10:12               ` Duy Nguyen
  2019-04-29 15:14                 ` Phillip Wood
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-04-25 10:12 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Elijah Newren, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On Thu, Apr 25, 2019 at 5:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
> > --discard-changes is a better name than --force for this option since
> > it's what really happens.
>
> I didn't realize when I suggested the name that --force overwrites
> untracked files as well as discarding changes from tracked files. I
> think we should document that. It would be nice if read-tree --reset -u
> took an optional argument so read-tree --reset=tracked -u would not
> overwrite untracked files. Then we could have --discard-changes just
> discard the changes and not overwrite untracked files. I had a quick
> look at unpack trees and it looks like a fairly straight forward change
> (famous last words) - perhaps I'll have a go at it next week.

So, --discard-changes is all about tracked changes, and we may have
--overwrite-untracked to cover the other part, and --force enables
both? That does not sound so bad (and maybe a good cure for those
"overwriting untracked" reports we've seen quite often lately).

Good luck with unpack-trees.c. But if it turns out you're too busy,
just let me know if want to hand that back to me.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'
  2019-03-29 15:35                           ` Elijah Newren
@ 2019-04-25 10:20                             ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-04-25 10:20 UTC (permalink / raw)
  To: Elijah Newren, Phillip Wood
  Cc: Duy Nguyen, Git Mailing List, Junio C Hamano, Eric Sunshine,
	SZEDER Gábor, Martin Ågren

Hi Elijah

Sorry it's taken me so long to reply to this - I'm not sure where the
last few weeks have gone.

On 29/03/2019 15:35, Elijah Newren wrote:
> Hi Phillip,
> 
> On Fri, Mar 29, 2019 at 4:04 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 28/03/2019 17:39, Elijah Newren wrote:
>>> On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>>> On 28/03/2019 11:04, Duy Nguyen wrote:
> 
>>>>> Just so we're clear, what is your "the way" to go? to remove
>>>>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if
>>>>> --ignore-in-process is specified? Or to leave MERGE_* and
>>>>> CHERRY_PICK_HEAD alone and delete other stuff?
>>>>
>>>> I was agreeing with Elijah about dropping --ignore-in-progress unless
>>>> there's a demand for it or at least restricting it so that it requires
>>>> --discard-changes and aborts in-progress merges and single in-progress
>>>> cherry-picks/reverts. (I'm worried about people switching branches when
>>>> cherry-picking more than one commit, though as you say it can make sense
>>>> during a rebase.)
>>>
>>> I understand the desire to prevent mis-uses, and I agree that if there
>>> are staged changes or conflicts it's really likely things will go
>>> sideways.  But I think we should instead check for those situations
>>> rather than use e.g. rebase vs. merge as a proxy for whether those
>>> problems could be present.
>>
>> When cherry-picking multiple commits if the user commits the conflict
>> resolution with 'git commit' then the presence of .git/sequencer is the
>> only sign that a cherry-pick is in progress (wt-status.c fails to detect
>> this, I've got a fix but no tests yet). rebase can also stop without
>> having conflicts or staged changes so I think we need to check for in
>> progress commands as well as conflicts (what do we want to do if someone
> 
> This whole discussion is about "--ignore-in-progress" which implicitly
> implies we are checking for in progress commands and choosing whether
> to override it.  So I don't understand what you mean by saying we need
> to check for it; isn't that a given?

I think I was confused "instead" above. Anyway we both agree that those
checks are in addition to the in-progress ones.

>> tries to switch in the middle of a bisect? - I don't have a strong
>> opinion). I agree switch should fail if there are conflicts, but I think
>> it is fine to switch with staged or unstaged changes if there isn't a
>> merge etc in progress (I quite often start working on something and
>> then realize I haven't started a new branch just before I commit). I
>> could possibly be convinced that silently switching with staged changes
>> is always a bad idea though.
> 
> I think we might be missing the big picture by trying to discuss
> things in terms of in-progress operations or conflicts or staged
> changes or unstaged changes.  Allow me to attempt to reframe the
> discussion:  We have identified at least one case where allowing the
> --ignore-in-progress flag would be unsafe, and we've identified one
> where we think it would be safe and useful, thus we need a rule of
> thumb for when it is safe to use and when it isn't.  Here's my
> attempt:
> 
>   --ignore-in-progress is safe enough for usage if we can switch to
> another branch and back with no net overall changes to either the
> index or the working tree after the two switches.

My worry is that someone will switch branches and commit by accident
before switching back again. Having said that we require them to specify
--ignore-in-progress so we have a safe default. I think your rule is
probably a good balance between safety and convenience, though it is
potentially confusing as it will allow --ignore-in-progress to switch
sometimes but not others.
> This rule could allow for the presence of both staged and unstaged
> changes (or maybe even conflicts in some alternate world where
> checkout/switch didn't necessarily error out on those), depending on
> if switch/checkout can operate without touching those particular files
> as part of switching.
> 
>>> I am especially concerned with the idea of
>>> having something like "git switch --ignore-in-progress
>>> --discard-changes" being used to quit merges or cherry-picks or
>>> reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git
>>> switch) in a way that is far less clear.  I think that's a bad route
>>> to go down, and we should keep the commands orthogonal
>>
>> keeping commands orthogonal is certainly clearer, if less convenient -
>> lets do it (assuming Duy agrees).
> 
> Yaay!
> 
>>> (if I could
>>> start all over, I'd also make reset and checkout and everything else
>>> stop modifying any in-progress state).
>>>
>>> Instead, I would either:
>>>
>>>    * Drop `--ignore-in-progress` for now.  (Although Duy had a
>>> meaningful usecase)
>>
>> I think it could be useful during a rebase, I'm not sure about any of
>> the other operations though.
> 
> I think it could be useful during some rebases, but it should not be
> allowed if the user can't switch back to the current commit with no
> net overall changes to the index or working tree.
> 
> Also, I don't see how rebase is unique here.  Rebase, cherry-pick,
> merge, and revert can all stop with conflicts, staged changes, and
> unstaged changes.  All of them can also stop without any one of those
> (e.g. cherry-pick'ing a commit which has been piecemeal applied
> already, merging a branch whose individual changes have already been
> cherry-picked and when the user has specified --no-commit, or
> reverting a commit whose changes have already been unapplied).  Thus,
> I continue to believe that which operation is in progress is
> irrelevant.  Either we shouldn't allow switching during any
> in-progress operation, or we should determine some other criteria for
> when it is safe to allow --ignore-in-progress.  Basing it on the
> operation would sometimes allow --ignore-in-progress to be used when
> it shouldn't be, and disallow it sometimes when it shouldn't.  I'm a
> fan of the rule I mentioned up above ("if we can switch and switch
> back with no net changes then it's safe enough to allow")

You're right that there is nothing intrinsically special about rebases,
it's just that in practice I think it is more useful in that case (I
don't remember wanting to switch branches while cherry-picking etc but
maybe that because I use them less). Having a consistent rule as you
suggest would be less confusing.

>>> OR
>>>
>>>    * Make `git switch --ignore-in-progress <branch>` leave all process
>>> state in place and switch branches, if we would otherwise be able to
>>> switch branches (i.e. there isn't dirty or conflicted changes in the
>>> way).
>>
>> I thought we allowed branch switches when there are staged or unstaged
>> changes, I don't think that is a problem unless we're in the middle of a
>> merge etc. I'm still not sure it's a good idea to switch branches in the
>> middle of a multiple cherry-pick, maybe we should print a warning.
> 
> I didn't say to disallow it if there were dirty or conflicted changes,
> I said to disallow it if there were dirty or conflicted changes *in
> the way*.  We don't allow branch switches when dirty changes would be
> overwritten or need to be merged, as that can't easily be reversed.  I
> think --ignore-in-progress should only be allowed when it can be
> easily reversed to get the user back to the right branch/commit.
> 
> This "no net changes" rule also reinforces (or is reinforced by) the
> other suggestion I made of having --ignore-in-progress be made
> incompatible with both -m and --discard-changes.
> 
> But I totally agree that switching branches during the middle of some
> operation should print a warning -- not just for cherry-pick, but for
> merge or rebase or revert too.  In all cases it'll be important to
> tell the user both that they could really mess things up if they try
> to resume the operation without switching back, and telling the user
> how to get back to where they used to be (in rebase's case, that'd be
> "git switch --ignore-in-progress <previous-commit>" while for the
> other three it'd be "git switch --ignore-in-progress
> <previous-branch>").

Giving the user instructions for how to switch back is a really good idea.

Best Wishes

Phillip
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 23/27] switch: reject if some operation is in progress
  2019-03-29 10:39           ` [PATCH v6 23/27] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
  2019-03-29 15:47             ` Elijah Newren
@ 2019-04-25 10:33             ` Phillip Wood
  2019-04-29  9:16               ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Phillip Wood @ 2019-04-25 10:33 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, rybak.a.v, sunshine, szeder.dev

On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
> Unless you know what you're doing, switching to another branch to do
> something then switching back could be confusing. Worse, you may even
> forget that you're in the middle of something. By the time you realize,
> you may have done a ton of work and it gets harder to go back.
> 
> A new option --ignore-in-progress was considered but dropped because it
> was not exactly clear what should happen. Sometimes you can switch away
> and get back safely and resume the operation. Sometimes not. And the
> git-checkout behavior is automatically clear merge/revert/cherry-pick,
> which makes it a bit even more confusing [1].
> 
> We may revisit and add this option in the future. But for now play it
> safe and not allow it (you can't even skip this check with --force).

I think this is a good compromise, lets see how it goes (I think I
broadly agree with Elijah's suggestion to allow the switch if we can
safely switch back again if we want to add --ignore-in-progress in the
future).

> The
> user is suggested to cancel the operation by themselves (and hopefully
> they do consider the consequences, not blindly type the command), or to
> create a separate worktree instead of switching. The third option is
> the good old "git checkout", but it's not mentioned.
> 
> [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 40 insertions(+)
> 
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index f7967cdb7c..5f100c1552 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -24,6 +24,7 @@
>  #include "tree.h"
>  #include "tree-walk.h"
>  #include "unpack-trees.h"
> +#include "wt-status.h"
>  #include "xdiff-interface.h"
>  
>  static const char * const checkout_usage[] = {
> @@ -56,6 +57,7 @@ struct checkout_opts {
>  	int accept_pathspec;
>  	int switch_branch_doing_nothing_is_ok;
>  	int only_merge_on_switching_branches;
> +	int can_switch_when_in_progress;
>  
>  	const char *new_branch;
>  	const char *new_branch_force;
> @@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
>  	die(_("a branch is expected, got '%s'"), branch_info->name);
>  }
>  
> +static void die_if_some_operation_in_progress(void)
> +{
> +	struct wt_status_state state;
> +
> +	memset(&state, 0, sizeof(state));
> +	wt_status_get_state(the_repository, &state, 0);
> +
> +	if (state.merge_in_progress)
> +		die(_("cannot switch branch while merging\n"
> +		      "Consider \"git merge --quit\" "
> +		      "or \"git worktree add\"."));

I'm not sure merge --quit exists, 'git grep \"quit origin/pu' shows
matches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the
sequencer command does not touch the index or working tree (that's the
difference between --quit and --abort) so the switch can still fail due
changes in the index and worktree that would be overwritten by the switch.

Best Wishes

Phillip

> +	if (state.am_in_progress)
> +		die(_("cannot switch branch in the middle of an am session\n"
> +		      "Consider \"git am --quit\" "
> +		      "or \"git worktree add\"."));
> +	if (state.rebase_interactive_in_progress || state.rebase_in_progress)
> +		die(_("cannot switch branch while rebasing\n"
> +		      "Consider \"git rebase --quit\" "
> +		      "or \"git worktree add\"."));
> +	if (state.cherry_pick_in_progress)
> +		die(_("cannot switch branch while cherry-picking\n"
> +		      "Consider \"git cherry-pick --quit\" "
> +		      "or \"git worktree add\"."));
> +	if (state.revert_in_progress)
> +		die(_("cannot switch branch while reverting\n"
> +		      "Consider \"git revert --quit\" "
> +		      "or \"git worktree add\"."));
> +	if (state.bisect_in_progress)
> +		die(_("cannot switch branch while bisecting\n"
> +		      "Consider \"git bisect reset HEAD\" "
> +		      "or \"git worktree add\"."));
> +}
> +
>  static int checkout_branch(struct checkout_opts *opts,
>  			   struct branch_info *new_branch_info)
>  {
> @@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,
>  	    !new_branch_info->path)
>  		die_expecting_a_branch(new_branch_info);
>  
> +	if (!opts->can_switch_when_in_progress)
> +		die_if_some_operation_in_progress();
> +
>  	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
>  	    !opts->ignore_other_worktrees) {
>  		int flag;
> @@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
>  	opts.only_merge_on_switching_branches = 0;
>  	opts.accept_pathspec = 1;
>  	opts.implicit_detach = 1;
> +	opts.can_switch_when_in_progress = 1;
>  
>  	options = parse_options_dup(checkout_options);
>  	options = add_common_options(&opts, options);
> @@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
>  	opts.switch_branch_doing_nothing_is_ok = 0;
>  	opts.only_merge_on_switching_branches = 1;
>  	opts.implicit_detach = 0;
> +	opts.can_switch_when_in_progress = 0;
>  
>  	options = parse_options_dup(switch_options);
>  	options = add_common_options(&opts, options);
> 


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 23/27] switch: reject if some operation is in progress
  2019-04-25 10:33             ` Phillip Wood
@ 2019-04-29  9:16               ` Duy Nguyen
  2019-04-29 15:10                 ` Phillip Wood
  0 siblings, 1 reply; 289+ messages in thread
From: Duy Nguyen @ 2019-04-29  9:16 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Elijah Newren, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On Thu, Apr 25, 2019 at 5:33 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
> > Unless you know what you're doing, switching to another branch to do
> > something then switching back could be confusing. Worse, you may even
> > forget that you're in the middle of something. By the time you realize,
> > you may have done a ton of work and it gets harder to go back.
> >
> > A new option --ignore-in-progress was considered but dropped because it
> > was not exactly clear what should happen. Sometimes you can switch away
> > and get back safely and resume the operation. Sometimes not. And the
> > git-checkout behavior is automatically clear merge/revert/cherry-pick,
> > which makes it a bit even more confusing [1].
> >
> > We may revisit and add this option in the future. But for now play it
> > safe and not allow it (you can't even skip this check with --force).
>
> I think this is a good compromise, lets see how it goes (I think I
> broadly agree with Elijah's suggestion to allow the switch if we can
> safely switch back again if we want to add --ignore-in-progress in the
> future).

I probably will revisit this topic much sooner than I thought. I did a
bisect today and found out "git switch" would not let me choose some
"random" commit to test, which I suspected more likely where the
problem was, or at least helped reduce the bisect steps. I had to go
back to "git checkout" and was not so happy.

This probably falls under the "safe to switch" (and not even back)
category, as long as switching does not destroy any data, since bisect
is basically jumping between commits with a clean worktree/index until
you find the right one.

> > +static void die_if_some_operation_in_progress(void)
> > +{
> > +     struct wt_status_state state;
> > +
> > +     memset(&state, 0, sizeof(state));
> > +     wt_status_get_state(the_repository, &state, 0);
> > +
> > +     if (state.merge_in_progress)
> > +             die(_("cannot switch branch while merging\n"
> > +                   "Consider \"git merge --quit\" "
> > +                   "or \"git worktree add\"."));
>
> I'm not sure merge --quit exists, 'git grep \"quit origin/pu' shows
> matches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the
> sequencer command does not touch the index or working tree (that's the
> difference between --quit and --abort) so the switch can still fail due
> changes in the index and worktree that would be overwritten by the switch.

Eck! Let me check if --abort is the same thing there or we need to add --quit...
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 23/27] switch: reject if some operation is in progress
  2019-04-29  9:16               ` Duy Nguyen
@ 2019-04-29 15:10                 ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-04-29 15:10 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Elijah Newren, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On 29/04/2019 10:16, Duy Nguyen wrote:
> On Thu, Apr 25, 2019 at 5:33 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
>>> Unless you know what you're doing, switching to another branch to do
>>> something then switching back could be confusing. Worse, you may even
>>> forget that you're in the middle of something. By the time you realize,
>>> you may have done a ton of work and it gets harder to go back.
>>>
>>> A new option --ignore-in-progress was considered but dropped because it
>>> was not exactly clear what should happen. Sometimes you can switch away
>>> and get back safely and resume the operation. Sometimes not. And the
>>> git-checkout behavior is automatically clear merge/revert/cherry-pick,
>>> which makes it a bit even more confusing [1].
>>>
>>> We may revisit and add this option in the future. But for now play it
>>> safe and not allow it (you can't even skip this check with --force).
>>
>> I think this is a good compromise, lets see how it goes (I think I
>> broadly agree with Elijah's suggestion to allow the switch if we can
>> safely switch back again if we want to add --ignore-in-progress in the
>> future).
> 
> I probably will revisit this topic much sooner than I thought. I did a
> bisect today and found out "git switch" would not let me choose some
> "random" commit to test, which I suspected more likely where the
> problem was, or at least helped reduce the bisect steps. I had to go
> back to "git checkout" and was not so happy.

Oh that's a pain, but should be safe as you describe below.

> This probably falls under the "safe to switch" (and not even back)
> category, as long as switching does not destroy any data, since bisect
> is basically jumping between commits with a clean worktree/index until
> you find the right one. >>> +static void die_if_some_operation_in_progress(void)
>>> +{
>>> +     struct wt_status_state state;
>>> +
>>> +     memset(&state, 0, sizeof(state));
>>> +     wt_status_get_state(the_repository, &state, 0);
>>> +
>>> +     if (state.merge_in_progress)
>>> +             die(_("cannot switch branch while merging\n"
>>> +                   "Consider \"git merge --quit\" "
>>> +                   "or \"git worktree add\"."));
>>
>> I'm not sure merge --quit exists, 'git grep \"quit origin/pu' shows
>> matches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the
>> sequencer command does not touch the index or working tree (that's the
>> difference between --quit and --abort) so the switch can still fail due
>> changes in the index and worktree that would be overwritten by the switch.
> 
> Eck! Let me check if --abort is the same thing there or we need to add --quit...

I think the --quit options generally leave the index and worktree alone, 
they just remove the state files whereas abort resets the index and 
worktree (with reset --mixed for merge and cherry-pick/revert, I think 
rebase does reset --hard) and also rewinds HEAD for rebases and 
sequences of cherry-picks and revert but that wont apply to merges as 
they are one-shot operations.

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 15/27] switch: add --discard-changes
  2019-04-25 10:12               ` Duy Nguyen
@ 2019-04-29 15:14                 ` Phillip Wood
  0 siblings, 0 replies; 289+ messages in thread
From: Phillip Wood @ 2019-04-29 15:14 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Eckhard Maaß,
	Git Mailing List, Junio C Hamano, Jacob Keller,
	Martin Ågren, Elijah Newren, Andrei Rybak, Eric Sunshine,
	SZEDER Gábor

On 25/04/2019 11:12, Duy Nguyen wrote:
> On Thu, Apr 25, 2019 at 5:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:
>>> --discard-changes is a better name than --force for this option since
>>> it's what really happens.
>>
>> I didn't realize when I suggested the name that --force overwrites
>> untracked files as well as discarding changes from tracked files. I
>> think we should document that. It would be nice if read-tree --reset -u
>> took an optional argument so read-tree --reset=tracked -u would not
>> overwrite untracked files. Then we could have --discard-changes just
>> discard the changes and not overwrite untracked files. I had a quick
>> look at unpack trees and it looks like a fairly straight forward change
>> (famous last words) - perhaps I'll have a go at it next week.
> 
> So, --discard-changes is all about tracked changes, and we may have
> --overwrite-untracked to cover the other part, and --force enables
> both? 

I was thinking of --discard-changes dealing with tracked changes and 
having --force for untracked changes as well. I'm not sure we need 
--overwrite-untracked for switch, just for read-tree.

That does not sound so bad (and maybe a good cure for those
> "overwriting untracked" reports we've seen quite often lately).
> 
> Good luck with unpack-trees.c. But if it turns out you're too busy,
> just let me know if want to hand that back to me.

Thanks, I've got something working, I'll clean it up and send it later 
in the week.

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-03-29 10:38           ` [PATCH v6 07/27] checkout: inform the user when removing branch state Nguyễn Thái Ngọc Duy
@ 2019-07-02  8:06             ` SZEDER Gábor
  2019-07-02  9:03               ` Duy Nguyen
  0 siblings, 1 reply; 289+ messages in thread
From: SZEDER Gábor @ 2019-07-02  8:06 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: eckhard.s.maass, git, gitster, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine

On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:
> After a successful switch, if a merge, cherry-pick or revert is ongoing,
> it is canceled. This behavior has been with us from the very early
> beginning, soon after git-merge was created but never actually
> documented [1]. It may be a good idea to be transparent and tell the
> user if some operation is canceled.

After this entered 'next' last week, today it greeted me with 167(!)
of these warnings...  before I even had my breakfast.

Now, my script does a lot of repeated cherry-picks and expects that
rerere is able to deal with most of the conflicts, i.e. it does
approximately this:

  if ! git cherry-pick $oid >/dev/null 2>&1
  then
      if was_the_conflict_resolved
      then
          echo "using previous conflict resolution"
          git commit --no-edit --cleanup=strip --quiet
      else
          die "uh-oh"
      fi
  fi

That 'git commit' in there always prints:

  warning: cancelling a cherry picking in progress

I don't understand why committing after a cherry-pick is considered
"cancelling"...  in my view it's finishing it and there should be no
warning whatsoever.

> I consider this a better way of telling the user than just adding a
> sentence or two in git-checkout.txt, which will be mostly ignored
> anyway.
> 
> PS. Originally I wanted to print more details like
> 
>     warning: cancelling an in-progress merge from <SHA-1>
> 
> which may allow some level of undo if the user wants to. But that seems
> a lot more work. Perhaps it can be improved later if people still want
> that.
> 
> [1] ... and I will try not to argue whether it is a sensible behavior.
> There is some more discussion here if people are interested:
> CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  branch.c           | 11 +++++++----
>  branch.h           |  2 +-
>  builtin/am.c       |  2 +-
>  builtin/checkout.c |  2 +-
>  builtin/rebase.c   |  4 ++--
>  builtin/reset.c    |  2 +-
>  builtin/revert.c   |  2 +-
>  7 files changed, 14 insertions(+), 11 deletions(-)
> 
> diff --git a/branch.c b/branch.c
> index 28b81a7e02..8dd5bb9f1c 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -337,11 +337,14 @@ void create_branch(struct repository *r,
>  	free(real_ref);
>  }
>  
> -void remove_branch_state(struct repository *r)
> +void remove_branch_state(struct repository *r, int verbose)
>  {
> -	unlink(git_path_cherry_pick_head(r));
> -	unlink(git_path_revert_head(r));
> -	unlink(git_path_merge_head(r));
> +	if (!unlink(git_path_cherry_pick_head(r)) && verbose)
> +		warning(_("cancelling a cherry picking in progress"));
> +	if (!unlink(git_path_revert_head(r)) && verbose)
> +		warning(_("cancelling a revert in progress"));
> +	if (!unlink(git_path_merge_head(r)) && verbose)
> +		warning(_("cancelling a merge in progress"));
>  	unlink(git_path_merge_rr(r));
>  	unlink(git_path_merge_msg(r));
>  	unlink(git_path_merge_mode(r));
> diff --git a/branch.h b/branch.h
> index 29c1afa4d0..aed045901e 100644
> --- a/branch.h
> +++ b/branch.h
> @@ -64,7 +64,7 @@ extern int validate_new_branchname(const char *name, struct strbuf *ref, int for
>   * Remove information about the state of working on the current
>   * branch. (E.g., MERGE_HEAD)
>   */
> -void remove_branch_state(struct repository *r);
> +void remove_branch_state(struct repository *r, int verbose);
>  
>  /*
>   * Configure local branch "local" as downstream to branch "remote"
> diff --git a/builtin/am.c b/builtin/am.c
> index 4fb107a9d1..99b66508fd 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1957,7 +1957,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
>  	if (merge_tree(remote_tree))
>  		return -1;
>  
> -	remove_branch_state(the_repository);
> +	remove_branch_state(the_repository, 0);
>  
>  	return 0;
>  }
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 0e6037b296..f66bd2f56d 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -899,7 +899,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
>  				delete_reflog(old_branch_info->path);
>  		}
>  	}
> -	remove_branch_state(the_repository);
> +	remove_branch_state(the_repository, !opts->quiet);
>  	strbuf_release(&msg);
>  	if (!opts->quiet &&
>  	    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 52114cbf0d..646d0f9fb1 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1272,7 +1272,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
>  			       NULL, NULL) < 0)
>  			die(_("could not discard worktree changes"));
> -		remove_branch_state(the_repository);
> +		remove_branch_state(the_repository, 0);
>  		if (read_basic_state(&options))
>  			exit(1);
>  		goto run_rebase;
> @@ -1292,7 +1292,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  			       NULL, NULL) < 0)
>  			die(_("could not move back to %s"),
>  			    oid_to_hex(&options.orig_head));
> -		remove_branch_state(the_repository);
> +		remove_branch_state(the_repository, 0);
>  		ret = finish_rebase(&options);
>  		goto cleanup;
>  	}
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 7882829a95..6d9397c844 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -420,7 +420,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
>  	}
>  	if (!pathspec.nr)
> -		remove_branch_state(the_repository);
> +		remove_branch_state(the_repository, 0);
>  
>  	return update_ref_status;
>  }
> diff --git a/builtin/revert.c b/builtin/revert.c
> index a47b53ceaf..ebf2789225 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -196,7 +196,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
>  	if (cmd == 'q') {
>  		int ret = sequencer_remove_state(opts);
>  		if (!ret)
> -			remove_branch_state(the_repository);
> +			remove_branch_state(the_repository, 0);
>  		return ret;
>  	}
>  	if (cmd == 'c')
> -- 
> 2.21.0.479.g47ac719cd3
> 

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-07-02  8:06             ` SZEDER Gábor
@ 2019-07-02  9:03               ` Duy Nguyen
  2019-07-02 10:43                 ` SZEDER Gábor
  2019-07-02 17:51                 ` Junio C Hamano
  0 siblings, 2 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-07-02  9:03 UTC (permalink / raw)
  To: SZEDER Gábor, Junio C Hamano
  Cc: eckhard.s.maass, git, jacob.keller, martin.agren, newren,
	phillip.wood123, rybak.a.v, sunshine

On Tue, Jul 02, 2019 at 10:06:11AM +0200, SZEDER Gábor wrote:
> On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > After a successful switch, if a merge, cherry-pick or revert is ongoing,
> > it is canceled. This behavior has been with us from the very early
> > beginning, soon after git-merge was created but never actually
> > documented [1]. It may be a good idea to be transparent and tell the
> > user if some operation is canceled.
> 
> After this entered 'next' last week, today it greeted me with 167(!)
> of these warnings...  before I even had my breakfast.
> 
> Now, my script does a lot of repeated cherry-picks and expects that
> rerere is able to deal with most of the conflicts, i.e. it does
> approximately this:
> 
>   if ! git cherry-pick $oid >/dev/null 2>&1
>   then
>       if was_the_conflict_resolved
>       then
>           echo "using previous conflict resolution"
>           git commit --no-edit --cleanup=strip --quiet
>       else
>           die "uh-oh"
>       fi
>   fi
> 
> That 'git commit' in there always prints:
> 
>   warning: cancelling a cherry picking in progress
> 
> I don't understand why committing after a cherry-pick is considered
> "cancelling"...  in my view it's finishing it and there should be no
> warning whatsoever.
> 

I agree, this is not "canceling". I think this series causes conflicts
with pw/clean-sequencer-state-upon-final-commit and the warning is
accidentally enabled (partly my fault since I named the argument
"verbose").

Junio, in this conflict resolution (merging nd/switch-and-restore to
next), we should pass '0' instead of 'verbose' to
sequencer_post_commit_cleanup().

diff --cc builtin/commit.c
index 1921401117,fa5982cc86..145d50caf0
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a
  		die("%s", err.buf);
  	}
  
- 	sequencer_post_commit_cleanup(the_repository);
 -	unlink(git_path_cherry_pick_head(the_repository));
 -	unlink(git_path_revert_head(the_repository));
++	sequencer_post_commit_cleanup(the_repository, verbose);
  	unlink(git_path_merge_head(the_repository));
  	unlink(git_path_merge_msg(the_repository));
  	unlink(git_path_merge_mode(the_repository));
--
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-07-02  9:03               ` Duy Nguyen
@ 2019-07-02 10:43                 ` SZEDER Gábor
  2019-07-02 17:51                 ` Junio C Hamano
  1 sibling, 0 replies; 289+ messages in thread
From: SZEDER Gábor @ 2019-07-02 10:43 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Junio C Hamano, eckhard.s.maass, git, jacob.keller, martin.agren,
	newren, phillip.wood123, rybak.a.v, sunshine

On Tue, Jul 02, 2019 at 04:03:09PM +0700, Duy Nguyen wrote:
> On Tue, Jul 02, 2019 at 10:06:11AM +0200, SZEDER Gábor wrote:
> > On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:
> > > After a successful switch, if a merge, cherry-pick or revert is ongoing,
> > > it is canceled. This behavior has been with us from the very early
> > > beginning, soon after git-merge was created but never actually
> > > documented [1]. It may be a good idea to be transparent and tell the
> > > user if some operation is canceled.
> > 
> > After this entered 'next' last week, today it greeted me with 167(!)
> > of these warnings...  before I even had my breakfast.
> > 
> > Now, my script does a lot of repeated cherry-picks and expects that
> > rerere is able to deal with most of the conflicts, i.e. it does
> > approximately this:
> > 
> >   if ! git cherry-pick $oid >/dev/null 2>&1
> >   then
> >       if was_the_conflict_resolved
> >       then
> >           echo "using previous conflict resolution"
> >           git commit --no-edit --cleanup=strip --quiet
> >       else
> >           die "uh-oh"
> >       fi
> >   fi
> > 
> > That 'git commit' in there always prints:
> > 
> >   warning: cancelling a cherry picking in progress
> > 
> > I don't understand why committing after a cherry-pick is considered
> > "cancelling"...  in my view it's finishing it and there should be no
> > warning whatsoever.
> > 
> 
> I agree, this is not "canceling". I think this series causes conflicts
> with pw/clean-sequencer-state-upon-final-commit and the warning is
> accidentally enabled (partly my fault since I named the argument
> "verbose").
> 
> Junio, in this conflict resolution (merging nd/switch-and-restore to
> next), we should pass '0' instead of 'verbose' to
> sequencer_post_commit_cleanup().
> 
> diff --cc builtin/commit.c
> index 1921401117,fa5982cc86..145d50caf0
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a
>   		die("%s", err.buf);
>   	}
>   
> - 	sequencer_post_commit_cleanup(the_repository);
>  -	unlink(git_path_cherry_pick_head(the_repository));
>  -	unlink(git_path_revert_head(the_repository));
> ++	sequencer_post_commit_cleanup(the_repository, verbose);
>   	unlink(git_path_merge_head(the_repository));
>   	unlink(git_path_merge_msg(the_repository));
>   	unlink(git_path_merge_mode(the_repository));

Thanks, this indeed takes care of it.


^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-07-02  9:03               ` Duy Nguyen
  2019-07-02 10:43                 ` SZEDER Gábor
@ 2019-07-02 17:51                 ` Junio C Hamano
  2019-07-04 23:57                   ` Duy Nguyen
  1 sibling, 1 reply; 289+ messages in thread
From: Junio C Hamano @ 2019-07-02 17:51 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: SZEDER Gábor, eckhard.s.maass, git, jacob.keller,
	martin.agren, newren, phillip.wood123, rybak.a.v, sunshine

Duy Nguyen <pclouds@gmail.com> writes:

> I agree, this is not "canceling". I think this series causes conflicts
> with pw/clean-sequencer-state-upon-final-commit and the warning is
> accidentally enabled (partly my fault since I named the argument
> "verbose").
>
> Junio, in this conflict resolution (merging nd/switch-and-restore to
> next), we should pass '0' instead of 'verbose' to
> sequencer_post_commit_cleanup().

Thanks for an update to the merge-fix.  What should the name of the
parameter to the sequencer_post_commit_cleanup() funcion be then,
though?  Perhaps we'd want an update after the topic graduates to
the 'master' branch.

>
> diff --cc builtin/commit.c
> index 1921401117,fa5982cc86..145d50caf0
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a
>   		die("%s", err.buf);
>   	}
>   
> - 	sequencer_post_commit_cleanup(the_repository);
>  -	unlink(git_path_cherry_pick_head(the_repository));
>  -	unlink(git_path_revert_head(the_repository));
> ++	sequencer_post_commit_cleanup(the_repository, verbose);
>   	unlink(git_path_merge_head(the_repository));
>   	unlink(git_path_merge_msg(the_repository));
>   	unlink(git_path_merge_mode(the_repository));
> --
> Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

* Re: [PATCH v6 07/27] checkout: inform the user when removing branch state
  2019-07-02 17:51                 ` Junio C Hamano
@ 2019-07-04 23:57                   ` Duy Nguyen
  0 siblings, 0 replies; 289+ messages in thread
From: Duy Nguyen @ 2019-07-04 23:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: SZEDER Gábor, Eckhard Maaß,
	Git Mailing List, Jacob Keller, Martin Ågren, Elijah Newren,
	Phillip Wood, Andrei Rybak, Eric Sunshine

On Wed, Jul 3, 2019 at 12:51 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Duy Nguyen <pclouds@gmail.com> writes:
>
> > I agree, this is not "canceling". I think this series causes conflicts
> > with pw/clean-sequencer-state-upon-final-commit and the warning is
> > accidentally enabled (partly my fault since I named the argument
> > "verbose").
> >
> > Junio, in this conflict resolution (merging nd/switch-and-restore to
> > next), we should pass '0' instead of 'verbose' to
> > sequencer_post_commit_cleanup().
>
> Thanks for an update to the merge-fix.  What should the name of the
> parameter to the sequencer_post_commit_cleanup() funcion be then,
> though?  Perhaps we'd want an update after the topic graduates to
> the 'master' branch.

Definitely. I was thinking something with "warn" and "cancel", which
is more the the point. Probably warn_cancel_in_progress, or something.
-- 
Duy

^ permalink raw reply	[flat|nested] 289+ messages in thread

end of thread, other threads:[~2019-07-04 23:57 UTC | newest]

Thread overview: 289+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-30  9:48 [PATCH 00/19] Add new command "switch" Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
2019-01-31  0:50   ` Eric Sunshine
2019-01-31  5:29     ` Duy Nguyen
2019-01-31  6:24       ` Eric Sunshine
2019-01-31  7:20         ` Eric Sunshine
2019-01-31  7:43         ` Duy Nguyen
2019-01-31  7:51           ` Eric Sunshine
2019-01-31  9:21           ` [PATCH] git-commit.txt: better description what it does Nguyễn Thái Ngọc Duy
2019-01-31 10:49             ` Eric Sunshine
2019-01-31 11:04               ` Duy Nguyen
2019-02-01  0:21                 ` Eric Sunshine
2019-02-01 10:09             ` [PATCH v2] " Nguyễn Thái Ngọc Duy
2019-02-04 17:59               ` Junio C Hamano
2019-01-31 13:06       ` [PATCH 08/19] checkout: split part of it to new command switch SZEDER Gábor
2019-01-31 13:09   ` SZEDER Gábor
2019-02-08  8:46     ` Duy Nguyen
2019-01-30  9:48 ` [PATCH 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-01-31  7:03   ` Eric Sunshine
2019-01-30  9:48 ` [PATCH 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-01-31  7:18   ` Eric Sunshine
2019-01-30  9:48 ` [PATCH 18/19] completion: support switch Nguyễn Thái Ngọc Duy
2019-01-30  9:48 ` [PATCH 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-01-31  7:39   ` Eric Sunshine
2019-01-31 11:28     ` Duy Nguyen
2019-01-31 13:37 ` [PATCH 00/19] Add new command "switch" SZEDER Gábor
2019-01-31 14:51   ` Duy Nguyen
2019-01-31 18:23     ` Junio C Hamano
2019-02-02 11:06       ` Duy Nguyen
2019-01-31 18:13 ` Junio C Hamano
2019-02-08  9:03 ` [PATCH v2 " Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 01/19] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 02/19] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 04/19] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 05/19] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-02-15 22:38     ` Junio C Hamano
2019-02-08  9:03   ` [PATCH v2 07/19] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 08/19] checkout: split part of it to new command switch Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 09/19] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-02-11  4:02     ` Eric Sunshine
2019-02-08  9:03   ` [PATCH v2 10/19] switch: remove -l Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 11/19] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 12/19] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 13/19] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 14/19] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
2019-02-10  8:37     ` Eric Sunshine
2019-02-08  9:03   ` [PATCH v2 16/19] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-02-08  9:03   ` [PATCH v2 17/19] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-02-08  9:04   ` [PATCH v2 18/19] completion: support switch Nguyễn Thái Ngọc Duy
2019-02-08 14:19     ` SZEDER Gábor
2019-02-09  5:30       ` Duy Nguyen
2019-02-08  9:04   ` [PATCH v2 19/19] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-03-08  9:57   ` [PATCH v3 00/21] Add new command "switch" Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 01/21] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
2019-03-10  3:28       ` Eric Sunshine
2019-03-08  9:57     ` [PATCH v3 02/21] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 03/21] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 04/21] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
2019-03-09 12:35       ` Martin Ågren
2019-03-11  9:35         ` Duy Nguyen
2019-03-11 10:41           ` Martin Ågren
2019-03-08  9:57     ` [PATCH v3 05/21] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 06/21] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 07/21] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 08/21] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 09/21] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
2019-03-09 12:36       ` Martin Ågren
2019-03-10 22:57       ` Jacob Keller
2019-03-11 19:00         ` Elijah Newren
2019-03-11 11:16       ` Phillip Wood
2019-03-11 11:47         ` Duy Nguyen
2019-03-11 17:03           ` Phillip Wood
2019-03-12 11:54             ` Duy Nguyen
2019-03-11 17:24           ` Elijah Newren
2019-03-11 20:51             ` Phillip Wood
2019-03-11 22:04               ` Elijah Newren
2019-03-12 10:58                 ` Phillip Wood
2019-03-12 12:19               ` Duy Nguyen
2019-03-12 15:36                 ` Eric Sunshine
2019-03-12 16:51                   ` Elijah Newren
2019-03-12 17:28                     ` Eric Sunshine
2019-03-13  1:55                       ` Junio C Hamano
2019-03-14  9:17                       ` Duy Nguyen
2019-03-14 11:02                         ` Phillip Wood
2019-03-14 12:56                           ` Duy Nguyen
2019-03-14 14:46                         ` Elijah Newren
2019-03-18  2:03                           ` Junio C Hamano
2019-03-18 12:06                             ` Duy Nguyen
2019-03-18 19:10                             ` Elijah Newren
2019-03-20 12:04                               ` Duy Nguyen
2019-03-15  6:11                     ` Jacob Keller
2019-03-12 11:58             ` Duy Nguyen
2019-03-12 17:05               ` Elijah Newren
2019-03-14 10:42                 ` Phillip Wood
2019-03-14  3:29         ` Duy Nguyen
2019-03-14  5:59           ` Elijah Newren
2019-03-14  7:23             ` Junio C Hamano
2019-03-14 15:48               ` Elijah Newren
2019-03-14  4:39         ` Junio C Hamano
2019-03-14 14:13         ` Duy Nguyen
2019-03-17  6:00         ` [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index Nguyễn Thái Ngọc Duy
2019-03-18  3:58           ` Junio C Hamano
2019-03-18  9:24             ` Duy Nguyen
2019-03-18  9:40               ` Junio C Hamano
2019-03-18 11:38           ` [PATCH v2] " Nguyễn Thái Ngọc Duy
2019-03-18 15:40             ` Elijah Newren
2019-03-19 14:06             ` Phillip Wood
2019-03-20  0:41               ` Junio C Hamano
2019-03-20  9:50                 ` Duy Nguyen
2019-03-26 12:50         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
2019-03-26 15:01           ` Elijah Newren
2019-03-26 15:24             ` Duy Nguyen
2019-03-26 15:48               ` Elijah Newren
2019-03-26 15:57                 ` Duy Nguyen
2019-03-27 10:24                 ` Phillip Wood
2019-03-28 11:04                   ` Duy Nguyen
2019-03-28 15:05                     ` Elijah Newren
2019-03-28 16:23                     ` Phillip Wood
2019-03-28 17:39                       ` Elijah Newren
2019-03-29 11:04                         ` Phillip Wood
2019-03-29 11:27                           ` Duy Nguyen
2019-03-29 15:35                           ` Elijah Newren
2019-04-25 10:20                             ` Phillip Wood
2019-04-01  9:29                 ` Junio C Hamano
2019-03-11 17:54       ` Elijah Newren
2019-03-12 11:06         ` Phillip Wood
2019-03-12 16:43           ` Elijah Newren
2019-03-14 11:00             ` Phillip Wood
2019-03-19  9:39               ` [PATCH] checkout.txt: note about losing staged changes with --merge Nguyễn Thái Ngọc Duy
2019-03-19 11:24                 ` Phillip Wood
2019-03-20  0:23                 ` Junio C Hamano
2019-03-20  0:40                   ` Duy Nguyen
2019-03-20  1:19                     ` Junio C Hamano
2019-03-20  1:22                       ` Duy Nguyen
2019-03-20  1:50                         ` Junio C Hamano
2019-03-20 13:53                           ` Elijah Newren
2019-03-20 13:57                             ` Duy Nguyen
2019-03-21 13:46                               ` Elijah Newren
2019-03-21  0:38                             ` Junio C Hamano
2019-03-13 11:05         ` [PATCH v3 10/21] checkout: split part of it to new command 'switch' Duy Nguyen
2019-03-13 14:36           ` Elijah Newren
2019-03-08  9:57     ` [PATCH v3 11/21] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 12/21] switch: remove -l Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 13/21] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 14/21] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 15/21] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 16/21] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim Nguyễn Thái Ngọc Duy
2019-03-13 18:36       ` Eckhard Maaß
2019-03-15  8:19         ` Eric Sunshine
2019-03-15  9:29           ` Duy Nguyen
2019-03-18  4:11           ` Junio C Hamano
2019-03-16  3:59         ` Duy Nguyen
2019-03-08  9:57     ` [PATCH v3 18/21] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-03-10  3:43       ` Eric Sunshine
2019-03-08  9:57     ` [PATCH v3 19/21] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-03-10  3:59       ` Eric Sunshine
2019-03-10 10:09       ` Andrei Rybak
2019-03-08  9:57     ` [PATCH v3 20/21] completion: support switch Nguyễn Thái Ngọc Duy
2019-03-08  9:57     ` [PATCH v3 21/21] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-03-08 17:48     ` [PATCH v3 00/21] Add new command "switch" Ramsay Jones
2019-03-09 11:56       ` Duy Nguyen
2019-03-17 12:49     ` [PATCH v4 00/26] Add new command 'switch' Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-03-17 19:18         ` Martin Ågren
2019-03-17 12:49       ` [PATCH v4 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
2019-03-17 19:21         ` Martin Ågren
2019-03-17 12:49       ` [PATCH v4 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
2019-03-18 16:48         ` Elijah Newren
2019-03-17 12:49       ` [PATCH v4 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
2019-03-18 23:55         ` Eric Sunshine
2019-03-17 12:49       ` [PATCH v4 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
2019-03-20 11:16         ` Phillip Wood
2019-03-17 12:49       ` [PATCH v4 23/26] switch: --orphan defaults to empty tree as HEAD Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 25/26] completion: support switch Nguyễn Thái Ngọc Duy
2019-03-17 12:49       ` [PATCH v4 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-03-17 12:58       ` [PATCH v4 00/26] Add new command 'switch' Duy Nguyen
2019-03-18  4:54         ` Junio C Hamano
2019-03-18 12:13           ` Duy Nguyen
2019-03-19  0:43             ` Junio C Hamano
2019-03-21 13:16       ` [PATCH v5 " Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 01/26] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 02/26] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 03/26] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 04/26] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 06/26] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 07/26] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 08/26] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 09/26] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 11/26] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 12/26] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 13/26] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 14/26] switch: add --discard-changes Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 15/26] switch: remove -l Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 16/26] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 17/26] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 18/26] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 19/26] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 20/26] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 21/26] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 22/26] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 23/26] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 24/26] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 25/26] completion: support switch Nguyễn Thái Ngọc Duy
2019-03-21 13:16         ` [PATCH v5 26/26] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-03-22  8:00           ` Andrei Rybak
2019-03-22  9:12             ` Duy Nguyen
2019-03-21 13:32         ` [PATCH v5 00/26] Add new command 'switch' Elijah Newren
2019-03-22  4:26         ` Junio C Hamano
2019-03-22  7:30           ` Duy Nguyen
2019-03-29 10:38         ` [PATCH v6 00/27] " Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 01/27] git-checkout.txt: spell out --no-option Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 02/27] git-checkout.txt: fix one syntax line Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 03/27] doc: document --overwrite-ignore Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 04/27] git-checkout.txt: fix monospace typeset Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 05/27] t: rename t2014-switch.sh to t2014-checkout-switch.sh Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 06/27] checkout: advice how to get out of detached HEAD mode Nguyễn Thái Ngọc Duy
2019-03-29 10:38           ` [PATCH v6 07/27] checkout: inform the user when removing branch state Nguyễn Thái Ngọc Duy
2019-07-02  8:06             ` SZEDER Gábor
2019-07-02  9:03               ` Duy Nguyen
2019-07-02 10:43                 ` SZEDER Gábor
2019-07-02 17:51                 ` Junio C Hamano
2019-07-04 23:57                   ` Duy Nguyen
2019-03-29 10:39           ` [PATCH v6 08/27] checkout: keep most #include sorted Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 09/27] checkout: factor out some code in parse_branchname_arg() Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 10/27] checkout: make "opts" in cmd_checkout() a pointer Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 11/27] checkout: move 'confict_style' and 'dwim_..' to checkout_opts Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 12/27] checkout: split options[] array in three pieces Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 13/27] checkout: split part of it to new command 'switch' Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 14/27] switch: better names for -b and -B Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 15/27] switch: add --discard-changes Nguyễn Thái Ngọc Duy
2019-04-25 10:02             ` Phillip Wood
2019-04-25 10:12               ` Duy Nguyen
2019-04-29 15:14                 ` Phillip Wood
2019-03-29 10:39           ` [PATCH v6 16/27] switch: remove -l Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 17/27] switch: stop accepting pathspec Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 18/27] switch: reject "do nothing" case Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 19/27] switch: only allow explicit detached HEAD Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 20/27] switch: add short option for --detach Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 21/27] switch: implicit dwim, use --no-guess to disable it Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 22/27] switch: no worktree status unless real branch switch happens Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 23/27] switch: reject if some operation is in progress Nguyễn Thái Ngọc Duy
2019-03-29 15:47             ` Elijah Newren
2019-04-25 10:33             ` Phillip Wood
2019-04-29  9:16               ` Duy Nguyen
2019-04-29 15:10                 ` Phillip Wood
2019-03-29 10:39           ` [PATCH v6 24/27] switch: make --orphan switch to an empty tree Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 25/27] t: add tests for switch Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 26/27] completion: support switch Nguyễn Thái Ngọc Duy
2019-03-29 10:39           ` [PATCH v6 27/27] doc: promote "git switch" Nguyễn Thái Ngọc Duy
2019-03-29 15:58           ` [PATCH v6 00/27] Add new command 'switch' Elijah Newren

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.