git@vger.kernel.org list mirror (unofficial, one of many)
 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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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 &#