{"thread":{"id":"50360","subject":"[PATCH 00/19] Add new command \"switch\"","startedAt":"2019-01-30T09:48:39Z","lastAt":"2019-07-04T23:57:31Z","messageCount":289,"participants":["Nguyễn Thái Ngọc Duy","Eric Sunshine","Duy Nguyen","SZEDER Gábor","Junio C Hamano","Ramsay Jones","Martin Ågren","Andrei Rybak","Jacob Keller","Phillip Wood","Elijah Newren","Eckhard Maaß"],"isPatch":true,"patchVersion":1,"patchTotal":19},"messages":[{"id":"368084","messageId":"20190130094831.10420-1-pclouds@gmail.com","threadId":"50360","inReplyTo":null,"subject":"[PATCH 00/19] Add new command \"switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:12Z","receivedAt":"2019-01-30T09:48:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the first half of the previous \"switch-branch and\nrestore-files\" RFC series [1]. I only focus on switch-branch for now\nbecause it's already getting long and I still have some work to do on\nrestore-files.\n\nThe command is, as the name implies, for switching branches. And it's\nsupposed to be a (hopefully better) alternative for \"git checkout\". Head\nto 08/19 for the man page, which describes the command (with\nexamples!) better than I do here.\n\nI think I've addressed most of the issues from the last round. The\nonly one left is adding options similar to \"git reset\" (but with\nbetter names this time than --soft and --hard, hopefully). I think\nthat could be done in a follow-up series.\n\nFor a complete picture, you would need to consider \"git restore\" too\n(the new name of \"git restore-files\"). Full series is available [2]\nthough I still need to work on the \"restore from a tree\" part of\nthat command and thinking whether \"restore -p\" vs \"reset -p\" could be\nconfusing...\n\nThis series is built on top of master + tg/checkout-no-overlay.\n\nPS. One of the things I like about this, after using it for some time,\nis the \"no ambiguation\" UI design gives better completion support\n(much less useless completion candidates), which makes life much\nbetter.\n\n[1] https://public-inbox.org/git/20181129215850.7278-1-pclouds@gmail.com/\n[2] https://gitlab.com/pclouds/git/commits/switch-and-restore\n\nNguyễn Thái Ngọc Duy (19):\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command switch\n  switch: better names for -b and -B\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: no implicit dwim, use --guess to dwim\n  switch: no worktree status unless real branch switch happens\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |   8 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                |  48 +-\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   4 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt                  | 236 +++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  18 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  52 +-\n Makefile                                      |   1 +\n advice.c                                      |  11 +-\n builtin.h                                     |   1 +\n builtin/checkout.c                            | 464 +++++++++---------\n builtin/clone.c                               |   2 +-\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  26 +\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   3 +-\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |   4 +-\n t/t2060-switch.sh                             |  91 ++++\n 39 files changed, 770 insertions(+), 363 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368085","messageId":"20190130094831.10420-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 01/19] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:13Z","receivedAt":"2019-01-30T09:48:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ce7d38b8b7..bc7fbdb8d2 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368086","messageId":"20190130094831.10420-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 02/19] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:14Z","receivedAt":"2019-01-30T09:48:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 5 +++++\n Documentation/git-merge.txt    | 4 ++++\n 2 files changed, 9 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex bc7fbdb8d2..9ac01f7967 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -270,6 +270,11 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--[no-]overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use --no-overwrite-ignore to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --[no-]recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..0480e9d0f0 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,10 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--[no-]overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use --no-overwrite-ignore to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368087","messageId":"20190130094831.10420-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:15Z","receivedAt":"2019-01-30T09:48:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368088","messageId":"20190130094831.10420-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 04/19] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:16Z","receivedAt":"2019-01-30T09:48:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f63d864a91..12b7e08d4e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1068,6 +1068,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1075,10 +1103,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1200,26 +1226,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368089","messageId":"20190130094831.10420-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 05/19] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:17Z","receivedAt":"2019-01-30T09:49:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 12b7e08d4e..66f99e788d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1326,82 +1326,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1409,14 +1410,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1425,7 +1426,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1444,56 +1445,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1512,6 +1513,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368090","messageId":"20190130094831.10420-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:18Z","receivedAt":"2019-01-30T09:49:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 66f99e788d..5914dd3619 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -46,6 +46,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -57,6 +59,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1329,8 +1332,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1355,12 +1357,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1378,6 +1380,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1386,7 +1389,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1394,9 +1397,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368091","messageId":"20190130094831.10420-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 07/19] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:19Z","receivedAt":"2019-01-30T09:49:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  3 +-\n 3 files changed, 78 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5914dd3619..8ea1349cce 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1327,15 +1327,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1345,34 +1361,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1386,6 +1417,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex e2f3eaed07..76eb5c211b 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 14fe32428e..6846a5b1e0 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -237,7 +237,8 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n extern int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n-extern struct option *parse_options_concat(struct option *a, struct option *b);\n+struct option *parse_options_dup(const struct option *a);\n+struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n extern int parse_opt_abbrev_cb(const struct option *, const char *, int);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368092","messageId":"20190130094831.10420-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 08/19] checkout: split part of it to new command switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:20Z","receivedAt":"2019-01-30T09:49:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and\nsomething-to-checkout-paths. The good old \"git checkout\" command is\nstill here and will be until all (or most of users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                        |   1 +\n Documentation/config/advice.txt   |  13 +-\n Documentation/config/branch.txt   |   4 +-\n Documentation/config/checkout.txt |   9 +-\n Documentation/config/diff.txt     |   3 +-\n Documentation/git-checkout.txt    |   4 +\n Documentation/git-switch.txt      | 236 ++++++++++++++++++++++++++++++\n Documentation/gitattributes.txt   |   3 +-\n Documentation/githooks.txt        |   8 +-\n Makefile                          |   1 +\n builtin.h                         |   1 +\n builtin/checkout.c                |  60 ++++++--\n command-list.txt                  |   1 +\n git.c                             |   1 +\n 14 files changed, 318 insertions(+), 27 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..8e2b89047f 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex 73380a8d86..fc211eca58 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 9ac01f7967..31c6cbef19 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -556,6 +556,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..953c9747b2\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,236 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--guess] <branch>\n+'git switch' [<options>] --detach [<start_point>>]\n+'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch and update files in the working tree to\n+match it. Optionally a new branch could be created with either `-c` or\n+`-C`, or detach from any branch with `--detach`, along with switching.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new_branch>::\n+\tName for the new branch.\n+\n+<start_point>::\n+\tThe name of a commit at which to switch to before creating a\n+\tnew branch or detach from.\n++\n+You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+branch/commit switched to \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `\"@{-1}`.\n++\n+As a special case, you may use `\"A...B\"` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new_branch>::\n+--create <new_branch>::\n+\tCreate a new branch named <new_branch> and start it at\n+\t<start_point>; see linkgit:git-branch[1] for details.\n+\n+-C <new_branch>::\n+--force-create <new_branch>::\n+\tCreates the branch <new_branch> and start it at <start_point>;\n+\tif it already exists, then reset it to <start_point>. This is\n+\tequivalent to running \"git branch\" with \"-f\"; see\n+\tlinkgit:git-branch[1] for details.\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+-g::\n+--guess::\n+\tIf <branch> is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it <remote>) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+-f::\n+--force::\n+\tProceed even if the index or the working tree differs from\n+\tHEAD.  This is used to throw away local changes.\n+\n+--[no-]progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use \"hack\" as\n+the local branch when branching off of \"origin/hack\" (or\n+\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\tbranch.autoSetupMerge configuration variable is true.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as --merge option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\tmerge.conflictStyle configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+--orphan <new_branch>::\n+\tCreate a new 'orphan' branch, named <new_branch>, started from\n+\t<start_point> and switch to it. See explanation of the same\n+\toption in linkgit:git-checkout[1] for details.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--[no-]recurse-submodules::\n+\tUsing --recurse-submodules will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name, use `-g`:\n+\n+------------\n+$ git switch -g new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit \"HEAD~3\" for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex b8392fc330..5731caa4c6 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 959044347e..e257a7810a 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -161,12 +161,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -402,7 +403,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 6e8d017e8e..0e5b29390c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -789,6 +789,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8ea1349cce..2ac53b4302 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1396,33 +1401,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1555,3 +1552,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 0ce0e13f0f..31b8e8d6a1 100644\n--- a/git.c\n+++ b/git.c\n@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368093","messageId":"20190130094831.10420-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 09/19] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:21Z","receivedAt":"2019-01-30T09:49:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout\nout. Move -b/-B to cmd_checkout() and new -c/-C with the same\nfunctionality in cmd_switch_branch()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2ac53b4302..2251883a88 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a new branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368094","messageId":"20190130094831.10420-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 10/19] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:22Z","receivedAt":"2019-01-30T09:49:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2251883a88..db9a2692db 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1357,7 +1357,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1558,6 +1557,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368095","messageId":"20190130094831.10420-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 11/19] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:23Z","receivedAt":"2019-01-30T09:49:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex db9a2692db..7906e07352 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -52,6 +52,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1162,10 +1163,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1199,11 +1206,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1248,7 +1256,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1564,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1591,6 +1600,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368096","messageId":"20190130094831.10420-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 12/19] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:24Z","receivedAt":"2019-01-30T09:49:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 7906e07352..9a58fccd71 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1319,6 +1320,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"nothing to do\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1572,6 +1579,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1601,6 +1609,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368097","messageId":"20190130094831.10420-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 13/19] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:25Z","receivedAt":"2019-01-30T09:49:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 +++++++++++\n 1 file changed, 11 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9a58fccd71..69df3a1547 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1326,6 +1327,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"nothing to do\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1581,6 +1590,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1610,6 +1620,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368098","messageId":"20190130094831.10420-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 14/19] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:26Z","receivedAt":"2019-01-30T09:49:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 69df3a1547..49c7ac211e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1381,7 +1381,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368099","messageId":"20190130094831.10420-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 15/19] switch: no implicit dwim, use --guess to dwim","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:27Z","receivedAt":"2019-01-30T09:49:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Similar to automatic detach, this behavior could be confusing because\nit can sometimes create a new branch without a user asking it to,\nespecially when the user is still not aware about this feature.\n\nIn the future, perhaps we could have a config key to disable these\nsafety nets and let 'switch' do automatic detach or dwim\nagain. But that will be opt-in after the user knows what is what. For\nnow give a short option if you want to use it often.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 37 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++--------\n 2 files changed, 30 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 31c6cbef19..bcb7822c27 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t<branch>.\n +\n If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it <remote>) with a matching name and\n+--no-guess is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \tbranch.autoSetupMerge configuration variable is true.\n \n+--[no-]guess::\n+\tIf <branch> is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it <remote>) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -284,10 +295,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach the\n \tsubmodules HEAD.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --[no-]overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 49c7ac211e..cdd07f6aba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -52,7 +52,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \n@@ -1388,8 +1388,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1426,7 +1424,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1441,7 +1438,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1501,7 +1497,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1582,12 +1578,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1612,12 +1610,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a new branch\")),\n+\t\tOPT_BOOL('g', \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368100","messageId":"20190130094831.10420-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 16/19] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:28Z","receivedAt":"2019-01-30T09:49:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 127 +++---------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 9 insertions(+), 140 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex fc211eca58..2fe24049fe 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -17,14 +17,6 @@ and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n \n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\n-\n checkout.overlayMode::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cdd07f6aba..8b4aabd6db 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -25,8 +25,6 @@\n #include \"submodule.h\"\n #include \"advice.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -55,11 +53,7 @@ struct checkout_opts {\n \tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -556,104 +550,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1011,6 +907,8 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n+\n \tmemset(&old_branch_info, 0, sizeof(old_branch_info));\n \told_branch_info.path = path_to_free = resolve_refdup(\"HEAD\", 0, &rev, &flag);\n \tif (old_branch_info.path)\n@@ -1027,16 +925,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1058,11 +952,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n \tstruct checkout_opts *opts = cb;\n \n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"checkout.overlaymode\")) {\n \t\topts->overlay_mode = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1587,6 +1476,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1620,6 +1510,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368101","messageId":"20190130094831.10420-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 17/19] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:29Z","receivedAt":"2019-01-30T09:50:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t2060-switch.sh | 91 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 91 insertions(+)\n create mode 100755 t/t2060-switch.sh\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..252f78c79e\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,91 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch to a commit' '\n+\ttest_must_fail git switch master^{commit}\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\tgit rev-parse master^ >expected &&\n+\tgit rev-parse refs/heads/temp >actual &&\n+\ttest_cmp expected actual &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\tgit switch -C temp &&\n+\tgit rev-parse master >expected &&\n+\tgit rev-parse refs/heads/temp >actual &&\n+\ttest_cmp expected actual &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --orphan new-orphan master^ &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_commit expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch foo &&\n+\tgit switch --guess foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368102","messageId":"20190130094831.10420-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 18/19] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:30Z","receivedAt":"2019-01-30T09:50:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 499e56f83d..891abb72d7 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2126,6 +2126,32 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if ---guess was specified to enable DWIM mode\n+\t\tlocal track_opt= only_local_ref=n\n+\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\telif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y ]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368103","messageId":"20190130094831.10420-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH 19/19] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-30T09:48:31Z","receivedAt":"2019-01-30T09:50:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt           |  8 ++--\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 +++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 18 ++++-----\n Documentation/giteveryday.txt          | 24 ++++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 52 +++++++++++++-------------\n advice.c                               | 11 ++++--\n builtin/clone.c                        |  2 +-\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             |  4 +-\n 18 files changed, 91 insertions(+), 86 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex bf5316ffa9..94c1f24414 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -293,7 +293,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -319,7 +319,7 @@ NOTES\n -----\n \n If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n+easier to use the \"git switch\" command with its `-c` option to create\n a branch and check it out with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 27304428a1..3d42853529 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4dd5853d6e..420777c10b 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 9f69ae8b69..020ba838c9 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..6bd0f192cc 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git reset --hard master^2\n-$ git checkout master\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..38015026bb 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch pu && git reset --hard next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..766e64d0e1 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n+The `git switch` command normally expects a branch head, but will also\n accept an arbitrary commit; for example, you can check out the commit\n referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..835219b51b 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: checking out '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 7c7f98c72c..b06f0ed735 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -491,7 +491,7 @@ static enum {\n static const char junk_leave_repo_msg[] =\n N_(\"Clone succeeded, but checkout failed.\\n\"\n    \"You can inspect what was checked out with 'git status'\\n\"\n-   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n+   \"and retry the checkout with 'git switch -f HEAD'\\n\");\n \n static void remove_junk(void)\n {\ndiff --git a/sha1-name.c b/sha1-name.c\nindex a656481c6a..e5f0832995 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -745,7 +745,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..50f7d6c15c 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -204,7 +204,7 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \tIf you want to create a new branch to retain commits you create, you may\n \tdo so (now or later) by using -b with the checkout command again. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit three\n \tEOF\n@@ -280,7 +280,7 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \tIf you want to create a new branch to retain commits you create, you may\n \tdo so (now or later) by using -b with the checkout command again. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit... three\n \tEOF\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368210","messageId":"CAPig+cQ3L4NcMojWYV3spazJNzEa6yhBJQ0wwbwcLSzG3Vmzjw@mail.gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-9-pclouds@gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T00:50:05Z","receivedAt":"2019-01-31T00:50:19Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> \"git checkout\" doing too many things is a source of confusion for many\n> users (and it even bites old timers sometimes). To remedy that, the\n> command will be split into two new ones: switch and\n> something-to-checkout-paths. The good old \"git checkout\" command is\n> still here and will be until all (or most of users) are sick of it.\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\n> @@ -0,0 +1,236 @@\n> +SYNOPSIS\n> +'git switch' [<options>] [--guess] <branch>\n> +'git switch' [<options>] --detach [<start_point>>]\n> +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n\nWhat does the third form mean when all optional arguments (that is,\n_all_ arguments) are omitted?\n\nNit: I realize you inherited this from git-checkout documentation, but\ncan you use '-' rather than '_', as in <start-point>, <new-branch>, to\nbe consistent with other documentation, such as git-branch.\n\n> +EXAMPLES\n> +To check out commit \"HEAD~3\" for temporary inspection or experiment\n> +without creating a new branch:\n> +\n> +------------\n> +$ git switch --detach HEAD~3\n> +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n> +------------\n> +\n> +If it turns out whatever you have done is worth keeping, you can\n> +always create a new name for it (without switching away):\n> +\n> +------------\n> +$ git switch -c good-surprises\n> +------------\n\nI had to go re-read git-branch documentation to understand why you\nrecommended \"git switch -c <new-branch>\" (or even why this mode\nexists) over plain \"git branch <new-branch>\". I wonder if the\ndifference between the two deserves a bit more discussion here and\nabove in the description of the -c/-C options.\n"},{"id":"368212","messageId":"CACsJy8BKxf6Q1Q-u92enPgw6a18XhAOvYSewZs-G+tB+-MSkNw@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cQ3L4NcMojWYV3spazJNzEa6yhBJQ0wwbwcLSzG3Vmzjw@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T05:29:01Z","receivedAt":"2019-01-31T05:29:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > \"git checkout\" doing too many things is a source of confusion for many\n> > users (and it even bites old timers sometimes). To remedy that, the\n> > command will be split into two new ones: switch and\n> > something-to-checkout-paths. The good old \"git checkout\" command is\n> > still here and will be until all (or most of users) are sick of it.\n> > [...]\n> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> > ---\n> > diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\n> > @@ -0,0 +1,236 @@\n> > +SYNOPSIS\n> > +'git switch' [<options>] [--guess] <branch>\n> > +'git switch' [<options>] --detach [<start_point>>]\n> > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n>\n> What does the third form mean when all optional arguments (that is,\n> _all_ arguments) are omitted?\n\n\"git switch\" is smart (or too dumb to be clever):\n\n$ git switch\nfatal: nothing to do\n\n> Nit: I realize you inherited this from git-checkout documentation, but\n> can you use '-' rather than '_', as in <start-point>, <new-branch>, to\n> be consistent with other documentation, such as git-branch.\n\nA quick grep shows <some-thing> seems to win over <some_thing>. Yeah.\n\n> > +EXAMPLES\n> > +To check out commit \"HEAD~3\" for temporary inspection or experiment\n> > +without creating a new branch:\n> > +\n> > +------------\n> > +$ git switch --detach HEAD~3\n> > +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n> > +------------\n> > +\n> > +If it turns out whatever you have done is worth keeping, you can\n> > +always create a new name for it (without switching away):\n> > +\n> > +------------\n> > +$ git switch -c good-surprises\n> > +------------\n>\n> I had to go re-read git-branch documentation to understand why you\n> recommended \"git switch -c <new-branch>\" (or even why this mode\n> exists) over plain \"git branch <new-branch>\". I wonder if the\n> difference between the two deserves a bit more discussion here and\n> above in the description of the -c/-C options.\n\nThere is this bit in git-branch, will that help?\n\n-- 8< --\nNOTES\n-----\n\nIf you are creating a branch that you want to checkout immediately, it is\neasier to use the \"git switch\" command with its `-c` option to create\na branch and check it out with a single command.\n-- 8< --\n\nOtherwise any suggestions are welcome, I guess I could rephrase it a bit like\n\n-c <new_branch>::\n--create <new_branch>::\n        Create a new branch named <new_branch> starting at\n        <start_point> before switching to the branch. This is similar\n        to creating a new branch with linkgit:git-branch[1] except\n        that `git branch` does not switch branches.\n-- \nDuy\n"},{"id":"368215","messageId":"CAPig+cS4Dmgh+hCMXp8ND5DMy6QFVCJOgvDwpcbwZ0HV4MscpA@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8BKxf6Q1Q-u92enPgw6a18XhAOvYSewZs-G+tB+-MSkNw@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T06:24:48Z","receivedAt":"2019-01-31T06:25:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 31, 2019 at 12:29 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > > +'git switch' [<options>] [--guess] <branch>\n> > > +'git switch' [<options>] --detach [<start_point>>]\n> > > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n> >\n> > What does the third form mean when all optional arguments (that is,\n> > _all_ arguments) are omitted?\n>\n> \"git switch\" is smart (or too dumb to be clever):\n>\n> $ git switch\n> fatal: nothing to do\n\nBut does it need to be this way? Does it make a good user-experience?\nI, personally, find it confusing to see that it can accept no\narguments. An alternative, perhaps more consistent with UX elsewhere:\n\n    $ git switch\n    fatal: missing branch argument\n\nor something.\n\n> > > +If it turns out whatever you have done is worth keeping, you can\n> > > +always create a new name for it (without switching away):\n> > > +------------\n> > > +$ git switch -c good-surprises\n> > > +------------\n> >\n> > I had to go re-read git-branch documentation to understand why you\n> > recommended \"git switch -c <new-branch>\" (or even why this mode\n> > exists) over plain \"git branch <new-branch>\". I wonder if the\n> > difference between the two deserves a bit more discussion here and\n> > above in the description of the -c/-C options.\n>\n> There is this bit in git-branch, will that help?\n>\n> -- 8< --\n> If you are creating a branch that you want to checkout immediately, it is\n> easier to use the \"git switch\" command with its `-c` option to create\n> a branch and check it out with a single command.\n> -- 8< --\n\nThat's pretty reasonable when reading git-branch documentation but\ndoesn't help when reading git-switch documentation.\n\n(Also, shouldn't the bit about \"want to checkout\" be changed to \"want\nto switch to\"?)\n\n> Otherwise any suggestions are welcome, I guess I could rephrase it a bit like\n>\n> -c <new_branch>::\n> --create <new_branch>::\n>         Create a new branch named <new_branch> starting at\n>         <start_point> before switching to the branch. This is similar\n>         to creating a new branch with linkgit:git-branch[1] except\n>         that `git branch` does not switch branches.\n\nThis is much better.\n\nI find, however, that the top-level git-switch \"DESCRIPTION\" section,\nwhich talks about \"switching branches\" doesn't actually ever explain\nwhat it means to \"switch\" to a branch. Even adding a simple sentence\nstating that \"switching to a branch means that a newly-created commit\nwill be a direct child of the current head of the branch, and that the\nbranch will be updated to point at the new commit\" would help cement\nthe meaning of branch switching in the reader's mind (rather than\nassuming the reader understands that implicitly).\n"},{"id":"368217","messageId":"CAPig+cQODhU4ep21UQ0gf+qOLWTmkj5k69ww1W3sENOFEYk7iw@mail.gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-10-pclouds@gmail.com","subject":"Re: [PATCH 09/19] switch: better names for -b and -B","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T07:03:32Z","receivedAt":"2019-01-31T07:03:47Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The shortcut of these options do not make much sense when used with\n> switch. And their descriptions are also tied to checkout\n> out. Move -b/-B to cmd_checkout() and new -c/-C with the same\n> functionality in cmd_switch_branch()\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> @@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n> @@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>  int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  {\n> +       struct option checkout_options[] = {\n> +               OPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n> +                          N_(\"create and checkout a new branch\")),\n> +               OPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n> +                          N_(\"create/reset and checkout a branch\")),\n\nNote wording for -B and compare with...\n\n> @@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n> +       struct option switch_options[] = {\n> +               OPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n> +                          N_(\"create and switch to a new branch\")),\n> +               OPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n> +                          N_(\"create/reset and switch to a new branch\")),\n\nwording for -C which talks about \"new branch\" rather than just\n\"branch\" of -B. (Dropping \"new\" from the latter makes it read a bit\nbetter, in my opinion, and would make it consistent with -B wording.)\n\n> +               OPT_END()\n> +       };\n"},{"id":"368218","messageId":"CAPig+cQfzv+zocqRZVC6hYywmPMR=p496weocdxBd6ng_7Cuug@mail.gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-18-pclouds@gmail.com","subject":"Re: [PATCH 17/19] t: add tests for switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T07:18:21Z","receivedAt":"2019-01-31T07:18:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jan 30, 2019 at 4:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\n> @@ -0,0 +1,91 @@\n> +test_expect_success 'switch and create branch' '\n> +       test_when_finished git switch master &&\n> +       git switch -c temp master^ &&\n> +       git rev-parse master^ >expected &&\n> +       git rev-parse refs/heads/temp >actual &&\n> +       test_cmp expected actual &&\n\nThe above three lines could be replaced with test_cmp_rev()?\n\n> +       echo refs/heads/temp >expected-branch &&\n> +       git symbolic-ref HEAD >actual-branch &&\n> +       test_cmp expected-branch actual-branch\n> +'\n> +\n> +test_expect_success 'force create branch from HEAD' '\n> +       test_when_finished git switch master &&\n> +       git switch --detach master &&\n> +       git switch -C temp &&\n> +       git rev-parse master >expected &&\n> +       git rev-parse refs/heads/temp >actual &&\n> +       test_cmp expected actual &&\n\nDitto: test_cmp_rev()\n\n> +       echo refs/heads/temp >expected-branch &&\n> +       git symbolic-ref HEAD >actual-branch &&\n> +       test_cmp expected-branch actual-branch\n> +'\n"},{"id":"368219","messageId":"CAPig+cR1bpNE3LUB1jOhDsCJA8FqF9kkTUmSrKFPOW6d6hpBPw@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cS4Dmgh+hCMXp8ND5DMy6QFVCJOgvDwpcbwZ0HV4MscpA@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T07:20:56Z","receivedAt":"2019-01-31T07:21:10Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 31, 2019 at 1:24 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Thu, Jan 31, 2019 at 12:29 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > On Thu, Jan 31, 2019 at 7:50 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > On Wed, Jan 30, 2019 at 4:49 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > > > +'git switch' [<options>] [--guess] <branch>\n> > > > +'git switch' [<options>] --detach [<start_point>>]\n> > > > +'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n> > >\n> > > What does the third form mean when all optional arguments (that is,\n> > > _all_ arguments) are omitted?\n> >\n> > \"git switch\" is smart (or too dumb to be clever):\n> >\n> > $ git switch\n> > fatal: nothing to do\n>\n> But does it need to be this way? Does it make a good user-experience?\n> I, personally, find it confusing to see that it can accept no\n> arguments. An alternative, perhaps more consistent with UX elsewhere:\n>\n>     $ git switch\n>     fatal: missing branch argument\n>\n> or something.\n\nLet me clarify by saying that I don't understand why the third form is\ndocumented as validly accepting no arguments given that a no-argument\ninvocation is an error. That is, I would expect the third form of the\nsynopsis to say:\n\n    'git switch' [<options>] (-c|-C|--orphan) <new_branch> [<start_point>]\n"},{"id":"368222","messageId":"CAPig+cRJJtZv0LcchXhV5FFOnbJTVio9Y_R8hyvGJEAGLsJ69A@mail.gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-20-pclouds@gmail.com","subject":"Re: [PATCH 19/19] doc: promote \"git switch\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T07:39:19Z","receivedAt":"2019-01-31T07:39:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jan 30, 2019 at 4:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The new command \"git switch\" is added to avoid the confusion of\n> one-command-do-all \"git checkout\" for new users. They are also helpful\n> to avoid ambiguation context.\n>\n> For these reasons, promote it everywhere possible. This includes\n> documentation, suggestions/advice from other commands...\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n> @@ -319,7 +319,7 @@ NOTES\n>  If you are creating a branch that you want to checkout immediately, it is\n\ns/checkout/switch to/\n\n> -easier to use the git checkout command with its `-b` option to create\n> +easier to use the \"git switch\" command with its `-c` option to create\n>  a branch and check it out with a single command.\n> diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\n> @@ -302,22 +302,22 @@ ref: refs/heads/master\n>  Examining an old version without creating a new branch\n>  ------------------------------------------------------\n>\n> -The `git checkout` command normally expects a branch head, but will also\n> +The `git switch` command normally expects a branch head, but will also\n>  accept an arbitrary commit; for example, you can check out the commit\n>  referenced by a tag:\n\nThis may need to mention --detach explicitly. Perhaps:\n\n    ...normally expects a branch head, but will also accept\n    an arbitrary commit when invoked with --detach;...\n\n> diff --git a/advice.c b/advice.c\n> @@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)\n>  void detach_advice(const char *new_name)\n>  {\n>         const char *fmt =\n> -       _(\"Note: checking out '%s'.\\n\\n\"\n> +       _(\"Note: checking out '%s'.\\n\"\n\nDo you also want to adjust this to avoid saying \"checking out\"?\n\n> +       \"\\n\"\n>         \"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n>         \"changes and commit them, and you can discard any commits you make in this\\n\"\n> -       \"state without impacting any branches by performing another checkout.\\n\\n\"\n> +       \"state without impacting any branches by performing another checkout.\\n\"\n\nDitto: \"checkout\"?\n\n> +       \"\\n\"\n>         \"If you want to create a new branch to retain commits you create, you may\\n\"\n> -       \"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n> -       \"  git checkout -b <new-branch-name>\\n\\n\");\n> +       \"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n\ns/-b/-c/\ns/checkout/switch/\n\n> +       \"\\n\"\n> +       \"  git switch -c <new-branch-name>\\n\\n\");\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> @@ -491,7 +491,7 @@ static enum {\n>  static const char junk_leave_repo_msg[] =\n>  N_(\"Clone succeeded, but checkout failed.\\n\"\n>     \"You can inspect what was checked out with 'git status'\\n\"\n> -   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n> +   \"and retry the checkout with 'git switch -f HEAD'\\n\");\n\nJust wondering if these uses of \"checkout\" and \"checked out\" need\nadjustment. I don't have any good suggestions, though.\n\n> diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\n> @@ -204,7 +204,7 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\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\ns/-b/-c/\ns/checkout/switch/\n\n> -         git checkout -b <new-branch-name>\n> +         git switch -c <new-branch-name>\n> @@ -280,7 +280,7 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\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\ns/-b/-c/\ns/checkout/switch/\n\n> -         git checkout -b <new-branch-name>\n> +         git switch -c <new-branch-name>\n"},{"id":"368224","messageId":"CACsJy8C2fx-+NxS+ahmmHDgLrwtyiCV6WEEfpmYwfcPM3kYVvA@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cS4Dmgh+hCMXp8ND5DMy6QFVCJOgvDwpcbwZ0HV4MscpA@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T07:43:41Z","receivedAt":"2019-01-31T07:44:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 1:25 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> I find, however, that the top-level git-switch \"DESCRIPTION\" section,\n> which talks about \"switching branches\" doesn't actually ever explain\n> what it means to \"switch\" to a branch. Even adding a simple sentence\n> stating that \"switching to a branch means that a newly-created commit\n> will be a direct child of the current head of the branch, and that the\n> branch will be updated to point at the new commit\" would help cement\n> the meaning of branch switching in the reader's mind (rather than\n> assuming the reader understands that implicitly).\n\nThanks. How about this? I skipped the \"update branch to point to the\nnew commit\" because that sounds like something you should learn from\ngit-commit and hopefully the word \"commit\" would be enough to recall\nthat knowledge (or direct the user to git-commit.txt). I notice\ngit-commit.txt does not say anything about branch update business\nthough. Maybe some more updates there...\n\nDESCRIPTION\n-----------\nSwitch to a specified branch. The working three and the index are also\nupdated to match the branch. All new commits will be added to the tip\nof this branch.\n\nOptionally a new branch could be created with either `-c` or `-C`, or\ndetach the working tree from any branch with `--detach`, along with\nswitching.\n\n\n-- \nDuy\n"},{"id":"368225","messageId":"CAPig+cRpxXA1uv_OqX9R9h6auBWE5=iS8CsxdB4qS6VaEqUadA@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8C2fx-+NxS+ahmmHDgLrwtyiCV6WEEfpmYwfcPM3kYVvA@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T07:51:13Z","receivedAt":"2019-01-31T07:51:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 31, 2019 at 2:44 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Thu, Jan 31, 2019 at 1:25 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > I find, however, that the top-level git-switch \"DESCRIPTION\" section,\n> > which talks about \"switching branches\" doesn't actually ever explain\n> > what it means to \"switch\" to a branch. Even adding a simple sentence\n> > stating that \"switching to a branch means that a newly-created commit\n> > will be a direct child of the current head of the branch, and that the\n> > branch will be updated to point at the new commit\" would help cement\n> > the meaning of branch switching in the reader's mind (rather than\n> > assuming the reader understands that implicitly).\n>\n> Thanks. How about this? I skipped the \"update branch to point to the\n> new commit\" ...\n\nI came to the same conclusion (about dropping this bit) upon\nre-reading what I wrote.\n\n> DESCRIPTION\n> -----------\n> Switch to a specified branch. The working three and the index are also\n> updated to match the branch. All new commits will be added to the tip\n> of this branch.\n\nYes, this is good. Simple and direct. A couple minor fixes:\n\ns/three/tree/\ns/also//\n"},{"id":"368229","messageId":"20190131092112.6197-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"CACsJy8C2fx-+NxS+ahmmHDgLrwtyiCV6WEEfpmYwfcPM3kYVvA@mail.gmail.com","subject":"[PATCH] git-commit.txt: better description what it does","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T09:21:12Z","receivedAt":"2019-01-31T09:21:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The description of git-commit jumps right into the commit content, which\nis important, but it fails to mention how the commit is \"added\" to the\nrepository. Update the first paragraph saying a bit more about branch\nupdate to fill this gap.\n\nWhile at there, add a couple linkgit references when the command is\nfirst mentioned.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On Thu, Jan 31, 2019 at 02:43:41PM +0700, Duy Nguyen wrote:\n > I notice git-commit.txt does not say anything about branch update\n > business though. Maybe some more updates there...\n\n Maybe something like this. Although I feel I'm quite close to some\n rabbit hole.\n\nDocumentation/git-commit.txt | 29 ++++++++++++++++++++++-------\n 1 file changed, 22 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex f970a43422..8148ba10b3 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -17,16 +17,19 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Stores the current contents of the index in a new commit along\n-with a log message from the user describing the changes.\n+Creates a new commit containing the current contents of the index with\n+a log message from the user describing the changes. The commit is the\n+direct child of the tip of the current branch. The branch is updated\n+to point to the new commit (unless no branch is associated with the\n+working tree, see \"NOTES\" section).\n \n-The content to be added can be specified in several ways:\n+The content to be committed can be specified in several ways:\n \n-1. by using 'git add' to incrementally \"add\" changes to the\n-   index before using the 'commit' command (Note: even modified\n-   files must be \"added\");\n+1. by using linkgit:git-add[1] to incrementally \"add\" changes to the\n+   index before using the 'commit' command (Note: even modified files\n+   must be \"added\");\n \n-2. by using 'git rm' to remove files from the working tree\n+2. by using linkgit:git-rm[1] to remove files from the working tree\n    and the index, again before using the 'commit' command;\n \n 3. by listing files as arguments to the 'commit' command\n@@ -349,6 +352,18 @@ changes to tracked files.\n :git-commit: 1\n include::date-formats.txt[]\n \n+NOTES\n+-----\n+If a branch is associated with the working tree, 'HEAD' points to this\n+branch. When a new commit is created, the branch is updated to point\n+to the new commit. As a result, resolving 'HEAD' still gives the new\n+commit.\n+\n+If no branch is associated with the working tree (i.e. \"detached HEAD\"\n+as described in linkgit:git-checkout[1]), 'HEAD' records the object\n+name of the previous commit directly. When a new commit is created, it\n+will be updated to point to the new commit.\n+\n EXAMPLES\n --------\n When recording your own work, the contents of modified files in\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368232","messageId":"CAPig+cSzDSeJ7HypXTjMpn26LYygH_UFYwcPzthGLoEQYDNLcQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190131092112.6197-1-pclouds@gmail.com","subject":"Re: [PATCH] git-commit.txt: better description what it does","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-01-31T10:49:04Z","receivedAt":"2019-01-31T10:49:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The description of git-commit jumps right into the commit content, which\n> is important, but it fails to mention how the commit is \"added\" to the\n> repository. Update the first paragraph saying a bit more about branch\n> update to fill this gap.\n>\n> While at there, add a couple linkgit references when the command is\n> first mentioned.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\n> @@ -17,16 +17,19 @@ SYNOPSIS\n>  DESCRIPTION\n>  -----------\n> +Creates a new commit containing the current contents of the index with\n> +a log message from the user describing the changes. The commit is the\n> +direct child of the tip of the current branch. The branch is updated\n> +to point to the new commit (unless no branch is associated with the\n> +working tree, see \"NOTES\" section).\n\nOkay. The information about the branch being updated makes sense.\n\n> +NOTES\n> +-----\n> +If a branch is associated with the working tree, 'HEAD' points to this\n> +branch. When a new commit is created, the branch is updated to point\n> +to the new commit. As a result, resolving 'HEAD' still gives the new\n> +commit.\n\nI'm not sure I understand the purpose of the final sentence about HEAD\n\"still resolving\" when you were, just before that, talking about the\nbranch.\n\n> +If no branch is associated with the working tree (i.e. \"detached HEAD\"\n> +as described in linkgit:git-checkout[1]), 'HEAD' records the object\n> +name of the previous commit directly. When a new commit is created, it\n> +will be updated to point to the new commit.\n\nI'm having a hard time figuring out what these two paragraphs together\nwant to say. I _think_ they want to say that HEAD is updated\nautomatically to point at the latest commit, and that if a branch\npoints at HEAD, then the branch is is updated along with HEAD,\notherwise if no branch, then it's a \"detached HEAD\".\n"},{"id":"368233","messageId":"CACsJy8D_3-SrxEAjrndieXhxu=mt6VUpDGTfVu2nrDW6USsgPw@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cSzDSeJ7HypXTjMpn26LYygH_UFYwcPzthGLoEQYDNLcQ@mail.gmail.com","subject":"Re: [PATCH] git-commit.txt: better description what it does","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T11:04:23Z","receivedAt":"2019-01-31T11:04:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 5:49 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > The description of git-commit jumps right into the commit content, which\n> > is important, but it fails to mention how the commit is \"added\" to the\n> > repository. Update the first paragraph saying a bit more about branch\n> > update to fill this gap.\n> >\n> > While at there, add a couple linkgit references when the command is\n> > first mentioned.\n> >\n> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> > ---\n> > diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\n> > @@ -17,16 +17,19 @@ SYNOPSIS\n> >  DESCRIPTION\n> >  -----------\n> > +Creates a new commit containing the current contents of the index with\n> > +a log message from the user describing the changes. The commit is the\n> > +direct child of the tip of the current branch. The branch is updated\n> > +to point to the new commit (unless no branch is associated with the\n> > +working tree, see \"NOTES\" section).\n>\n> Okay. The information about the branch being updated makes sense.\n>\n> > +NOTES\n> > +-----\n> > +If a branch is associated with the working tree, 'HEAD' points to this\n> > +branch. When a new commit is created, the branch is updated to point\n> > +to the new commit. As a result, resolving 'HEAD' still gives the new\n> > +commit.\n>\n> I'm not sure I understand the purpose of the final sentence about HEAD\n> \"still resolving\" when you were, just before that, talking about the\n> branch.\n>\n> > +If no branch is associated with the working tree (i.e. \"detached HEAD\"\n> > +as described in linkgit:git-checkout[1]), 'HEAD' records the object\n> > +name of the previous commit directly. When a new commit is created, it\n> > +will be updated to point to the new commit.\n>\n> I'm having a hard time figuring out what these two paragraphs together\n> want to say. I _think_ they want to say that HEAD is updated\n> automatically to point at the latest commit, and that if a branch\n> points at HEAD, then the branch is is updated along with HEAD,\n> otherwise if no branch, then it's a \"detached HEAD\".\n\nI felt some more explanation was needed when I mentioned about\nupdating current branch in the description, but then what happens when\nthere's no branch (aka detached HEAD)? There is still some updates. If\nyou consider HEAD a branch, then it's ok, but otherwise something is\nmissing. But perhaps this is the kind of details that could be left\nout?\n-- \nDuy\n"},{"id":"368234","messageId":"CACsJy8DEeK7wbDJbk_mVkRjU2rFz7EvRyYRwf8N_8pt+sDKX3w@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cRJJtZv0LcchXhV5FFOnbJTVio9Y_R8hyvGJEAGLsJ69A@mail.gmail.com","subject":"Re: [PATCH 19/19] doc: promote \"git switch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T11:28:44Z","receivedAt":"2019-01-31T11:29:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 2:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > diff --git a/builtin/clone.c b/builtin/clone.c\n> > @@ -491,7 +491,7 @@ static enum {\n> >  static const char junk_leave_repo_msg[] =\n> >  N_(\"Clone succeeded, but checkout failed.\\n\"\n> >     \"You can inspect what was checked out with 'git status'\\n\"\n> > -   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n> > +   \"and retry the checkout with 'git switch -f HEAD'\\n\");\n>\n> Just wondering if these uses of \"checkout\" and \"checked out\" need\n> adjustment. I don't have any good suggestions, though.\n\nI think I'm going to leave this message alone for now. Technically\n\"git switch\" can replace \"git checkout\" here but I feel this is more\nabout restoring files on worktree than switching branches. So when\n\"git restore\" comes, I'll change it to say \"git restore --from=HEAD\n:/\" or something like that instead.\n-- \nDuy\n"},{"id":"368235","messageId":"20190131130638.GI13764@szeder.dev","threadId":"50360","inReplyTo":"CACsJy8BKxf6Q1Q-u92enPgw6a18XhAOvYSewZs-G+tB+-MSkNw@mail.gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-01-31T13:06:38Z","receivedAt":"2019-01-31T13:06:45Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Thu, Jan 31, 2019 at 12:29:01PM +0700, Duy Nguyen wrote:\n> > > +EXAMPLES\n> > > +To check out commit \"HEAD~3\" for temporary inspection or experiment\n> > > +without creating a new branch:\n> > > +\n> > > +------------\n> > > +$ git switch --detach HEAD~3\n> > > +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n> > > +------------\n> > > +\n> > > +If it turns out whatever you have done is worth keeping, you can\n> > > +always create a new name for it (without switching away):\n> > > +\n> > > +------------\n> > > +$ git switch -c good-surprises\n> > > +------------\n> >\n> > I had to go re-read git-branch documentation to understand why you\n> > recommended \"git switch -c <new-branch>\" (or even why this mode\n> > exists) over plain \"git branch <new-branch>\". I wonder if the\n> > difference between the two deserves a bit more discussion here and\n> > above in the description of the -c/-C options.\n> \n> There is this bit in git-branch, will that help?\n> \n> -- 8< --\n> NOTES\n> -----\n> \n> If you are creating a branch that you want to checkout immediately, it is\n> easier to use the \"git switch\" command with its `-c` option to create\n> a branch and check it out with a single command.\n> -- 8< --\n> \n> Otherwise any suggestions are welcome, I guess I could rephrase it a bit like\n> \n> -c <new_branch>::\n> --create <new_branch>::\n>         Create a new branch named <new_branch> starting at\n>         <start_point> before switching to the branch. This is similar\n>         to creating a new branch with linkgit:git-branch[1] except\n>         that `git branch` does not switch branches.\n\nI think the second sentence, in particular the \"except...\" part, in\nthis description goes backwards by explaining how 'git branch' differs\nfrom this.  I think the most to-the-point explanation instead of that\nsecond sentence would be something like this:\n\n  This is a shorthand for 'git branch <new-branch> [<start-point>] &&\n  git switch <new-branch>'.\n\n"},{"id":"368236","messageId":"20190131130953.GJ13764@szeder.dev","threadId":"50360","inReplyTo":"20190130094831.10420-9-pclouds@gmail.com","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-01-31T13:09:53Z","receivedAt":"2019-01-31T13:09:58Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Jan 30, 2019 at 04:48:20PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\n> new file mode 100644\n> index 0000000000..953c9747b2\n\n> +--orphan <new_branch>::\n> +\tCreate a new 'orphan' branch, named <new_branch>, started from\n> +\t<start_point> and switch to it. See explanation of the same\n> +\toption in linkgit:git-checkout[1] for details.\n\nCopy-paste error?  An orphan branch, by definition, doesn't have a\nstarting point.\n\n"},{"id":"368237","messageId":"20190131133731.GK13764@szeder.dev","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"Re: [PATCH 00/19] Add new command \"switch\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-01-31T13:37:31Z","receivedAt":"2019-01-31T13:37:37Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Jan 30, 2019 at 04:48:12PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> The\n> only one left is adding options similar to \"git reset\" (but with\n> better names this time than --soft and --hard, hopefully).\n\nOh no, what for?!  As the commit message or patch 19/19 'doc: promote\n\"git switch\"' states:\n\n  The new command \"git switch\" is added to avoid the confusion of\n  one-command-do-all \"git checkout\" for new users.\n\nAdding '--soft|--hard' from 'git reset' would go in the opposite\ndirection.\n\n"},{"id":"368241","messageId":"CACsJy8A7TjYWJsxXETUU31JzZL-aCXcr+rHsQ23Ec1ujeMcQTA@mail.gmail.com","threadId":"50360","inReplyTo":"20190131133731.GK13764@szeder.dev","subject":"Re: [PATCH 00/19] Add new command \"switch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-31T14:51:29Z","receivedAt":"2019-01-31T14:51:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 8:37 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:\n>\n> On Wed, Jan 30, 2019 at 04:48:12PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > The\n> > only one left is adding options similar to \"git reset\" (but with\n> > better names this time than --soft and --hard, hopefully).\n>\n> Oh no, what for?!  As the commit message or patch 19/19 'doc: promote\n> \"git switch\"' states:\n>\n>   The new command \"git switch\" is added to avoid the confusion of\n>   one-command-do-all \"git checkout\" for new users.\n>\n> Adding '--soft|--hard' from 'git reset' would go in the opposite\n> direction.\n\nIf it's about the confusion, I think we can avoid it. If it's about\nthe one-command-do-all, I think it still fits in the main topic of\ngit-switch, which is about switching. But in git-reset case it's\nswitching HEAD, not a normal branch.\n\nWhat I have in mind so far is this, let's see if it's so bad so I can\nfind another direction.\n\ngit switch --reset-branch [--keep-index] [--keep-worktree] [<commit>]\n\nThis updates worktree and index to <commit> and \"rewinds\" the current\nbranch to <commit>. If either of those --keep-* is given, that part\nwill be left untouched. So \"git reset --hard\" is the same as \"git\nswitch --reset-branch\", --soft \"--reset-branch --keep-index\n--keep-worktree\" and --mixed \"--reset-branch --keep-worktree\".\n\nI've been updating docs with this new format to see what it looks like\nand I think it expresses the idea behind git-reset much better.\n-- \nDuy\n"},{"id":"368252","messageId":"xmqqo97w3dca.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"Re: [PATCH 00/19] Add new command \"switch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-31T18:13:09Z","receivedAt":"2019-01-31T18:13:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is the first half of the previous \"switch-branch and\n> restore-files\" RFC series [1]. I only focus on switch-branch for now\n> because it's already getting long and I still have some work to do on\n> restore-files.\n\nExciting ;-)  This will probably become the best UI improvement in\nthe past few years.\n\n"},{"id":"368254","messageId":"xmqqk1ik3cvi.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8A7TjYWJsxXETUU31JzZL-aCXcr+rHsQ23Ec1ujeMcQTA@mail.gmail.com","subject":"Re: [PATCH 00/19] Add new command \"switch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-31T18:23:13Z","receivedAt":"2019-01-31T18:23:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> Adding '--soft|--hard' from 'git reset' would go in the opposite\n>> direction.\n>\n> If it's about the confusion, I think we can avoid it. If it's about\n> the one-command-do-all, I think it still fits in the main topic of\n> git-switch, which is about switching. But in git-reset case it's\n> switching HEAD, not a normal branch.\n\nI do not think \"switch branch\" should update HEAD for the branch.\nLet it be handled by \"reset\" (or \"branch -f that-other-branch\").\n\nI personally did not have \"it is way too overloaded\" problem with\n\"checkout\", but it turns out that many others found it so.  You'll\nsee the same happen for your \"switch\" if you do not resist\ntemptation to add unrelated things (or things you may find related\nbut you see others find unrelated even in this early discussion).\n"},{"id":"368286","messageId":"CAPig+cS0MnYebOvVtEWZEJcNgOgNfHgUg1F35x=tzrK1D1X3+A@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8D_3-SrxEAjrndieXhxu=mt6VUpDGTfVu2nrDW6USsgPw@mail.gmail.com","subject":"Re: [PATCH] git-commit.txt: better description what it does","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-02-01T00:21:05Z","receivedAt":"2019-02-01T00:21:19Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 31, 2019 at 6:04 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Thu, Jan 31, 2019 at 5:49 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Thu, Jan 31, 2019 at 4:21 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > > +NOTES\n> > > +-----\n> > > +If a branch is associated with the working tree, 'HEAD' points to this\n> > > +branch. When a new commit is created, the branch is updated to point\n> > > +to the new commit. As a result, resolving 'HEAD' still gives the new\n> > > +commit.\n> > > +\n> > > +If no branch is associated with the working tree (i.e. \"detached HEAD\"\n> > > +as described in linkgit:git-checkout[1]), 'HEAD' records the object\n> > > +name of the previous commit directly. When a new commit is created, it\n> > > +will be updated to point to the new commit.\n> >\n> > I'm having a hard time figuring out what these two paragraphs together\n> > want to say. I _think_ they want to say that HEAD is updated\n> > automatically to point at the latest commit, and that if a branch\n> > points at HEAD, then the branch is is updated along with HEAD,\n> > otherwise if no branch, then it's a \"detached HEAD\".\n>\n> I felt some more explanation was needed when I mentioned about\n> updating current branch in the description, but then what happens when\n> there's no branch (aka detached HEAD)? There is still some updates. If\n> you consider HEAD a branch, then it's ok, but otherwise something is\n> missing. But perhaps this is the kind of details that could be left\n> out?\n\nIt could be left out or incorporated directly into the DESCRIPTION\nsection with a link to the much more thorough discussion of detached\nHEAD in git-checkout documentation. Here's my attempt at rewriting\nDESCRIPTION (and dropping NOTES altogether):\n\n    Create a new commit containing the current contents of the index\n    and the given log message describing the changes. The new commit\n    is a direct child of HEAD, usually the tip of the current branch,\n    and the branch is updated to point to it (unless no branch is\n    associated with the working tree, in which case HEAD is \"detached\"\n    as described in linkgit:git-checkout[1]).\n\n(I also streamlined the above a bit from what you had composed.)\n"},{"id":"368320","messageId":"20190201100910.30432-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190131092112.6197-1-pclouds@gmail.com","subject":"[PATCH v2] git-commit.txt: better description what it does","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-01T10:09:10Z","receivedAt":"2019-02-01T10:09:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The description of git-commit jumps right into the commit content, which\nis important, but it fails to mention how the commit is \"added\" to the\nrepository. Update the first paragraph saying a bit more about branch\nupdate to fill this gap.\n\nWhile at there, add a couple linkgit references when the command is\nfirst mentioned.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n NOTES section is gone. First paragraph updated.\n\n Documentation/git-commit.txt | 18 +++++++++++-------\n 1 file changed, 11 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex f970a43422..a85c2c2a4c 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -17,16 +17,20 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Stores the current contents of the index in a new commit along\n-with a log message from the user describing the changes.\n+Create a new commit containing the current contents of the index and\n+the given log message describing the changes. The new commit is a\n+direct child of HEAD, usually the tip of the current branch, and the\n+branch is updated to point to it (unless no branch is associated with\n+the working tree, in which case HEAD is \"detached\" as described in\n+linkgit:git-checkout[1]).\n \n-The content to be added can be specified in several ways:\n+The content to be committed can be specified in several ways:\n \n-1. by using 'git add' to incrementally \"add\" changes to the\n-   index before using the 'commit' command (Note: even modified\n-   files must be \"added\");\n+1. by using linkgit:git-add[1] to incrementally \"add\" changes to the\n+   index before using the 'commit' command (Note: even modified files\n+   must be \"added\");\n \n-2. by using 'git rm' to remove files from the working tree\n+2. by using linkgit:git-rm[1] to remove files from the working tree\n    and the index, again before using the 'commit' command;\n \n 3. by listing files as arguments to the 'commit' command\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368404","messageId":"CACsJy8D6+X7b842D4ZDgsLiYHt9+akTPiVtrbjcc8zxJw38TUw@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqk1ik3cvi.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 00/19] Add new command \"switch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-02T11:06:17Z","receivedAt":"2019-02-02T11:06:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 1, 2019 at 1:23 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> >> Adding '--soft|--hard' from 'git reset' would go in the opposite\n> >> direction.\n> >\n> > If it's about the confusion, I think we can avoid it. If it's about\n> > the one-command-do-all, I think it still fits in the main topic of\n> > git-switch, which is about switching. But in git-reset case it's\n> > switching HEAD, not a normal branch.\n>\n> I do not think \"switch branch\" should update HEAD for the branch.\n> Let it be handled by \"reset\" (or \"branch -f that-other-branch\").\n\nIt can already, it's simply a shortcut for \"git switch --force-create\n<current-branch> -f <commit>\" (or \"git checkout -fB <current-branch>\n<commit>\"), at least for --hard. --mixed and --soft is just a\ndifferent variant of \"-f\".\n\n> I personally did not have \"it is way too overloaded\" problem with\n> \"checkout\", but it turns out that many others found it so.  You'll\n> see the same happen for your \"switch\" if you do not resist\n> temptation to add unrelated things (or things you may find related\n> but you see others find unrelated even in this early discussion).\n\nThe thing about git-reset is it's also overloaded with updating things\nother than HEAD. All the pathspec form is the same as checkout/restore\n(except checkout updates both index/worktree while reset is more about\nindex; but restore can cover all index/worktree combination). But yeah\nmaybe just leave it for now. While trying to document the new option\nin switch to replace reset, the verb \"rewind\" (the tip of the current\nbranch) seems fitting well. I might revisit this topic with git-rewind\nor something.\n-- \nDuy\n"},{"id":"368463","messageId":"xmqq7eefwi26.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190201100910.30432-1-pclouds@gmail.com","subject":"Re: [PATCH v2] git-commit.txt: better description what it does","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-04T17:59:45Z","receivedAt":"2019-02-04T17:59:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The description of git-commit jumps right into the commit content, which\n> is important, but it fails to mention how the commit is \"added\" to the\n> repository. Update the first paragraph saying a bit more about branch\n> update to fill this gap.\n>\n> While at there, add a couple linkgit references when the command is\n> first mentioned.\n>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  NOTES section is gone. First paragraph updated.\n\nI haven't been following the discussion in the previous round, but\nthis looks like a sensible change.  Thanks, both.\n\n\n>\n>  Documentation/git-commit.txt | 18 +++++++++++-------\n>  1 file changed, 11 insertions(+), 7 deletions(-)\n>\n> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\n> index f970a43422..a85c2c2a4c 100644\n> --- a/Documentation/git-commit.txt\n> +++ b/Documentation/git-commit.txt\n> @@ -17,16 +17,20 @@ SYNOPSIS\n>  \n>  DESCRIPTION\n>  -----------\n> -Stores the current contents of the index in a new commit along\n> -with a log message from the user describing the changes.\n> +Create a new commit containing the current contents of the index and\n> +the given log message describing the changes. The new commit is a\n> +direct child of HEAD, usually the tip of the current branch, and the\n> +branch is updated to point to it (unless no branch is associated with\n> +the working tree, in which case HEAD is \"detached\" as described in\n> +linkgit:git-checkout[1]).\n>  \n> -The content to be added can be specified in several ways:\n> +The content to be committed can be specified in several ways:\n>  \n> -1. by using 'git add' to incrementally \"add\" changes to the\n> -   index before using the 'commit' command (Note: even modified\n> -   files must be \"added\");\n> +1. by using linkgit:git-add[1] to incrementally \"add\" changes to the\n> +   index before using the 'commit' command (Note: even modified files\n> +   must be \"added\");\n>  \n> -2. by using 'git rm' to remove files from the working tree\n> +2. by using linkgit:git-rm[1] to remove files from the working tree\n>     and the index, again before using the 'commit' command;\n>  \n>  3. by listing files as arguments to the 'commit' command\n"},{"id":"368784","messageId":"CACsJy8D-vL88JaJYqhXnkJ5Cxvou_=haSFLpwELuX-E9cj_9aQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190131130953.GJ13764@szeder.dev","subject":"Re: [PATCH 08/19] checkout: split part of it to new command switch","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T08:46:27Z","receivedAt":"2019-02-08T08:46:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jan 31, 2019 at 8:09 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:\n>\n> On Wed, Jan 30, 2019 at 04:48:20PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\n> > new file mode 100644\n> > index 0000000000..953c9747b2\n>\n> > +--orphan <new_branch>::\n> > +     Create a new 'orphan' branch, named <new_branch>, started from\n> > +     <start_point> and switch to it. See explanation of the same\n> > +     option in linkgit:git-checkout[1] for details.\n>\n> Copy-paste error?  An orphan branch, by definition, doesn't have a\n> starting point.\n\nIt does not have any parents, true. The \"started from <start-point>\"\nis not super clear on this, but git-checkout.txt describes more and\nsays that it's essentially \"git checkout <start_point>\" then make an\norphan branch based on HEAD^{tree}.\n-- \nDuy\n"},{"id":"368786","messageId":"20190208090401.14793-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190130094831.10420-1-pclouds@gmail.com","subject":"[PATCH v2 00/19] Add new command \"switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:42Z","receivedAt":"2019-02-08T09:04:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series adds a new command \"switch\" for switching branches, an\nalternative for \"git checkout\".\n\nv2 fixes the comments I got from v1. I think the range-diff below\nsummarizes it better than I do. No major changes though.\n\nRange-diff dựa trên v1:\n 1:  6d550b443c !  1:  2283204090 checkout: split part of it to new command switch\n    @@ -148,24 +148,33 @@\n     +--------\n     +[verse]\n     +'git switch' [<options>] [--guess] <branch>\n    -+'git switch' [<options>] --detach [<start_point>>]\n    -+'git switch' [<options>] [[-c|-C|--orphan] <new_branch>] [<start_point>]\n    ++'git switch' [<options>] --detach [<start-point>]\n    ++'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n     +\n     +DESCRIPTION\n     +-----------\n    -+Switch to a specified branch and update files in the working tree to\n    -+match it. Optionally a new branch could be created with either `-c` or\n    -+`-C`, or detach from any branch with `--detach`, along with switching.\n    ++Switch to a specified branch. The working tree and the index are\n    ++updated to match the branch. All new commits will be added to the tip\n    ++of this branch.\n    ++\n    ++Optionally a new branch could be created with either `-c` or `-C`, or\n    ++detach the working tree from any branch with `--detach`, along with\n    ++switching.\n    ++\n    ++Switching branches does not require a clean index and working tree\n    ++(i.e. no differences compared to 'HEAD'). The operation is aborted\n    ++however if the switch leads to loss of local changes, unless told\n    ++otherwise.\n     +\n     +OPTIONS\n     +-------\n     +<branch>::\n     +\tBranch to switch to.\n     +\n    -+<new_branch>::\n    ++<new-branch>::\n     +\tName for the new branch.\n     +\n    -+<start_point>::\n    ++<start-point>::\n     +\tThe name of a commit at which to switch to before creating a\n     +\tnew branch or detach from.\n     ++\n    @@ -177,17 +186,27 @@\n     +base of `A` and `B` if there is exactly one merge base. You can leave\n     +out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n     +\n    -+-c <new_branch>::\n    -+--create <new_branch>::\n    -+\tCreate a new branch named <new_branch> and start it at\n    -+\t<start_point>; see linkgit:git-branch[1] for details.\n    ++-c <new-branch>::\n    ++--create <new-branch>::\n    ++\tCreate a new branch named <new-branch> starting at\n    ++\t<start-point> before switching to the branch. This is a\n    ++\tconvenient shortcut for:\n    +++\n    ++------------\n    ++$ git branch <new-branch>\n    ++$ git switch <new-branch>\n    ++------------\n     +\n    -+-C <new_branch>::\n    -+--force-create <new_branch>::\n    -+\tCreates the branch <new_branch> and start it at <start_point>;\n    -+\tif it already exists, then reset it to <start_point>. This is\n    -+\tequivalent to running \"git branch\" with \"-f\"; see\n    -+\tlinkgit:git-branch[1] for details.\n    ++-C <new-branch>::\n    ++--force-create <new-branch>::\n    ++\tSimilar to `--create` except that if <new-branch> already\n    ++\texists, it will be reset to <start-point>. This is a\n    ++\tconvenient shortcut for:\n    +++\n    ++------------\n    ++$ git branch -f <new-branch>\n    ++$ git switch <new-branch>\n    ++------------\n     +\n     +-d::\n     +--detach::\n    @@ -220,7 +239,9 @@\n     +-f::\n     +--force::\n     +\tProceed even if the index or the working tree differs from\n    -+\tHEAD.  This is used to throw away local changes.\n    ++\tHEAD. Both the index and working tree are restored to match\n    ++\tthe switching target. This is used to throw away local\n    ++\tchanges.\n     +\n     +--[no-]progress::\n     +\tProgress status is reported on the standard error stream\n    @@ -270,9 +291,9 @@\n     +\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n     +\t\"merge\" style, shows the original contents).\n     +\n    -+--orphan <new_branch>::\n    -+\tCreate a new 'orphan' branch, named <new_branch>, started from\n    -+\t<start_point> and switch to it. See explanation of the same\n    ++--orphan <new-branch>::\n    ++\tCreate a new 'orphan' branch, named <new-branch>, started from\n    ++\t<start-point> and switch to it. See explanation of the same\n     +\toption in linkgit:git-checkout[1] for details.\n     +\n     +--ignore-other-worktrees::\n 2:  e2f94df471 !  2:  deceb221dc switch: better names for -b and -B\n    @@ -61,7 +61,7 @@\n     +\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n     +\t\t\t   N_(\"create and switch to a new branch\")),\n     +\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n    -+\t\t\t   N_(\"create/reset and switch to a new branch\")),\n    ++\t\t\t   N_(\"create/reset and switch to a branch\")),\n     +\t\tOPT_END()\n     +\t};\n      \tint ret;\n 3:  b3d431a97f =  3:  08f28e9726 switch: remove -l\n 4:  26738818fa =  4:  9ee4852c8b switch: stop accepting pathspec\n 5:  79ebe7a68c !  5:  0a7ea4b33b switch: reject \"do nothing\" case\n    @@ -32,7 +32,7 @@\n     +\t    !new_branch_info->name &&\n     +\t    !opts->new_branch &&\n     +\t    !opts->force_detach)\n    -+\t\tdie(_(\"nothing to do\"));\n    ++\t\tdie(_(\"missing branch or commit argument\"));\n     +\n      \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n      \t    !opts->ignore_other_worktrees) {\n 6:  bc36ac8510 !  6:  eedf82c72b switch: only allow explicit detached HEAD\n    @@ -26,7 +26,7 @@\n      \tint ignore_skipworktree;\n     @@\n      \t    !opts->force_detach)\n    - \t\tdie(_(\"nothing to do\"));\n    + \t\tdie(_(\"missing branch or commit argument\"));\n      \n     +\tif (!opts->implicit_detach &&\n     +\t    !opts->force_detach &&\n 7:  1a761ce8e5 =  7:  9314ff16c9 switch: add short option for --detach\n 8:  738c560eb9 !  8:  9d8e461df2 switch: no implicit dwim, use --guess to dwim\n    @@ -143,7 +143,7 @@\n     @@\n      \t\t\t   N_(\"create and switch to a new branch\")),\n      \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n    - \t\t\t   N_(\"create/reset and switch to a new branch\")),\n    + \t\t\t   N_(\"create/reset and switch to a branch\")),\n     +\t\tOPT_BOOL('g', \"guess\", &opts.dwim_new_local_branch,\n     +\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n      \t\tOPT_END()\n 9:  456e9340f1 =  9:  749665c457 switch: no worktree status unless real branch switch happens\n10:  cdd132a7e2 ! 10:  e4e0b397eb t: add tests for switch\n    @@ -51,9 +51,7 @@\n     +test_expect_success 'switch and create branch' '\n     +\ttest_when_finished git switch master &&\n     +\tgit switch -c temp master^ &&\n    -+\tgit rev-parse master^ >expected &&\n    -+\tgit rev-parse refs/heads/temp >actual &&\n    -+\ttest_cmp expected actual &&\n    ++\ttest_cmp_rev master^ refs/heads/temp &&\n     +\techo refs/heads/temp >expected-branch &&\n     +\tgit symbolic-ref HEAD >actual-branch &&\n     +\ttest_cmp expected-branch actual-branch\n    @@ -63,9 +61,7 @@\n     +\ttest_when_finished git switch master &&\n     +\tgit switch --detach master &&\n     +\tgit switch -C temp &&\n    -+\tgit rev-parse master >expected &&\n    -+\tgit rev-parse refs/heads/temp >actual &&\n    -+\ttest_cmp expected actual &&\n    ++\ttest_cmp_rev master refs/heads/temp &&\n     +\techo refs/heads/temp >expected-branch &&\n     +\tgit symbolic-ref HEAD >actual-branch &&\n     +\ttest_cmp expected-branch actual-branch\n11:  96a97fcab4 = 11:  b9a7662271 completion: support switch\n12:  8d984bc684 ! 12:  7449a0c4cc doc: promote \"git switch\"\n    @@ -40,14 +40,18 @@\n      +\n      <1> This step and the next one could be combined into a single step with\n     @@\n    + NOTES\n      -----\n      \n    - If you are creating a branch that you want to checkout immediately, it is\n    +-If you are creating a branch that you want to checkout immediately, it is\n     -easier to use the git checkout command with its `-b` option to create\n    -+easier to use the \"git switch\" command with its `-c` option to create\n    - a branch and check it out with a single command.\n    +-a branch and check it out with a single command.\n    ++If you are creating a branch that you want to switch to immediately,\n    ++it is easier to use the \"git switch\" command with its `-c` option to\n    ++do the same thing with a single command.\n      \n      The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n    + serve four related but different purposes:\n     \n      diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\n      --- a/Documentation/git-check-ref-format.txt\n    @@ -473,9 +477,11 @@\n      ------------------------------------------------------\n      \n     -The `git checkout` command normally expects a branch head, but will also\n    +-accept an arbitrary commit; for example, you can check out the commit\n    +-referenced by a tag:\n     +The `git switch` command normally expects a branch head, but will also\n    - accept an arbitrary commit; for example, you can check out the commit\n    - referenced by a tag:\n    ++accept an arbitrary commit when invoked with --detach; for example,\n    ++you can check out the commit referenced by a tag:\n      \n      ------------------------------------------------\n     -$ git checkout v2.6.17\n    @@ -628,36 +634,23 @@\n      {\n      \tconst char *fmt =\n     -\t_(\"Note: checking out '%s'.\\n\\n\"\n    -+\t_(\"Note: checking out '%s'.\\n\"\n    ++\t_(\"Note: switching to '%s'.\\n\"\n     +\t\"\\n\"\n      \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n      \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n     -\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n    -+\t\"state without impacting any branches by performing another checkout.\\n\"\n    ++\t\"state without impacting any branches by switching back to a branch.\\n\"\n     +\t\"\\n\"\n      \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n     -\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n     -\t\"  git checkout -b <new-branch-name>\\n\\n\");\n    -+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n    ++\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n     +\t\"\\n\"\n     +\t\"  git switch -c <new-branch-name>\\n\\n\");\n      \n      \tfprintf(stderr, fmt, new_name);\n      }\n     \n    - diff --git a/builtin/clone.c b/builtin/clone.c\n    - --- a/builtin/clone.c\n    - +++ b/builtin/clone.c\n    -@@\n    - static const char junk_leave_repo_msg[] =\n    - N_(\"Clone succeeded, but checkout failed.\\n\"\n    -    \"You can inspect what was checked out with 'git status'\\n\"\n    --   \"and retry the checkout with 'git checkout -f HEAD'\\n\");\n    -+   \"and retry the checkout with 'git switch -f HEAD'\\n\");\n    - \n    - static void remove_junk(void)\n    - {\n    -\n      diff --git a/sha1-name.c b/sha1-name.c\n      --- a/sha1-name.c\n      +++ b/sha1-name.c\n    @@ -675,8 +668,20 @@\n      --- a/t/t2020-checkout-detach.sh\n      +++ b/t/t2020-checkout-detach.sh\n     @@\n    + \n    + \t# The first detach operation is more chatty than the following ones.\n    + \tcat >1st_detach <<-EOF &&\n    +-\tNote: checking out 'HEAD^'.\n    ++\tNote: switching to 'HEAD^'.\n    + \n    + \tYou are in 'detached HEAD' state. You can look around, make experimental\n    + \tchanges and commit them, and you can discard any commits you make in this\n    +-\tstate without impacting any branches by performing another checkout.\n    ++\tstate without impacting any branches by switching back to a branch.\n    + \n      \tIf you want to create a new branch to retain commits you create, you may\n    - \tdo so (now or later) by using -b with the checkout command again. Example:\n    +-\tdo so (now or later) by using -b with the checkout command again. Example:\n    ++\tdo so (now or later) by using -c with the switch command. Example:\n      \n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n    @@ -684,8 +689,20 @@\n      \tHEAD is now at \\$commit three\n      \tEOF\n     @@\n    + \n    + \t# The first detach operation is more chatty than the following ones.\n    + \tcat >1st_detach <<-EOF &&\n    +-\tNote: checking out 'HEAD^'.\n    ++\tNote: switching to 'HEAD^'.\n    + \n    + \tYou are in 'detached HEAD' state. You can look around, make experimental\n    + \tchanges and commit them, and you can discard any commits you make in this\n    +-\tstate without impacting any branches by performing another checkout.\n    ++\tstate without impacting any branches by switching back to a branch.\n    + \n      \tIf you want to create a new branch to retain commits you create, you may\n    - \tdo so (now or later) by using -b with the checkout command again. Example:\n    +-\tdo so (now or later) by using -b with the checkout command again. Example:\n    ++\tdo so (now or later) by using -c with the switch command. Example:\n      \n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n13:  91e5442069 <  -:  ---------- checkout: split part of it to new command restore\n14:  6bc8f87c0a <  -:  ---------- restore: take tree-ish from --from option instead\n15:  095a1cd3e3 <  -:  ---------- restore: make pathspec mandatory\n16:  6eb5612187 <  -:  ---------- restore: disable overlay mode by default\n17:  cc79522047 <  -:  ---------- t: add tests for restore\n18:  ed5d7097b6 <  -:  ---------- completion: support restore\n19:  625a4fac68 <  -:  ---------- doc: promote \"git restore\"\n\nNguyễn Thái Ngọc Duy (19):\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command switch\n  switch: better names for -b and -B\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: no implicit dwim, use --guess to dwim\n  switch: no worktree status unless real branch switch happens\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |  12 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                |  48 +-\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   4 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt                  | 257 ++++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  18 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  56 +--\n Makefile                                      |   1 +\n advice.c                                      |  11 +-\n builtin.h                                     |   1 +\n builtin/checkout.c                            | 464 +++++++++---------\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  26 +\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   3 +-\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |  16 +-\n t/t2060-switch.sh                             |  87 ++++\n 38 files changed, 796 insertions(+), 372 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368787","messageId":"20190208090401.14793-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 01/19] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:43Z","receivedAt":"2019-02-08T09:04:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ce7d38b8b7..bc7fbdb8d2 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368788","messageId":"20190208090401.14793-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 02/19] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:44Z","receivedAt":"2019-02-08T09:04:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 5 +++++\n Documentation/git-merge.txt    | 4 ++++\n 2 files changed, 9 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex bc7fbdb8d2..9ac01f7967 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -270,6 +270,11 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--[no-]overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use --no-overwrite-ignore to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --[no-]recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..0480e9d0f0 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,10 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--[no-]overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use --no-overwrite-ignore to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368789","messageId":"20190208090401.14793-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 03/19] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:45Z","receivedAt":"2019-02-08T09:04:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368790","messageId":"20190208090401.14793-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 04/19] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:46Z","receivedAt":"2019-02-08T09:04:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f63d864a91..12b7e08d4e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1068,6 +1068,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1075,10 +1103,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1200,26 +1226,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368791","messageId":"20190208090401.14793-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 05/19] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:47Z","receivedAt":"2019-02-08T09:04:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 12b7e08d4e..66f99e788d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1326,82 +1326,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1409,14 +1410,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1425,7 +1426,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1444,56 +1445,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1512,6 +1513,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368792","messageId":"20190208090401.14793-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:48Z","receivedAt":"2019-02-08T09:04:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 66f99e788d..5914dd3619 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -46,6 +46,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -57,6 +59,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1329,8 +1332,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1355,12 +1357,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1378,6 +1380,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1386,7 +1389,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1394,9 +1397,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368793","messageId":"20190208090401.14793-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 07/19] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:49Z","receivedAt":"2019-02-08T09:05:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  3 +-\n 3 files changed, 78 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5914dd3619..8ea1349cce 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1327,15 +1327,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1345,34 +1361,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1386,6 +1417,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex e2f3eaed07..76eb5c211b 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 14fe32428e..6846a5b1e0 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -237,7 +237,8 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n extern int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n-extern struct option *parse_options_concat(struct option *a, struct option *b);\n+struct option *parse_options_dup(const struct option *a);\n+struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n extern int parse_opt_abbrev_cb(const struct option *, const char *, int);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368794","messageId":"20190208090401.14793-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 08/19] checkout: split part of it to new command switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:50Z","receivedAt":"2019-02-08T09:05:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and\nsomething-to-checkout-paths. The good old \"git checkout\" command is\nstill here and will be until all (or most of users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                        |   1 +\n Documentation/config/advice.txt   |  13 +-\n Documentation/config/branch.txt   |   4 +-\n Documentation/config/checkout.txt |   9 +-\n Documentation/config/diff.txt     |   3 +-\n Documentation/git-checkout.txt    |   4 +\n Documentation/git-switch.txt      | 257 ++++++++++++++++++++++++++++++\n Documentation/gitattributes.txt   |   3 +-\n Documentation/githooks.txt        |   8 +-\n Makefile                          |   1 +\n builtin.h                         |   1 +\n builtin/checkout.c                |  60 +++++--\n command-list.txt                  |   1 +\n git.c                             |   1 +\n 14 files changed, 339 insertions(+), 27 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..8e2b89047f 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex 73380a8d86..fc211eca58 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 9ac01f7967..31c6cbef19 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -556,6 +556,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..1a4d156e3c\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,257 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--guess] <branch>\n+'git switch' [<options>] --detach [<start-point>]\n+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch. The working tree and the index are\n+updated to match the branch. All new commits will be added to the tip\n+of this branch.\n+\n+Optionally a new branch could be created with either `-c` or `-C`, or\n+detach the working tree from any branch with `--detach`, along with\n+switching.\n+\n+Switching branches does not require a clean index and working tree\n+(i.e. no differences compared to 'HEAD'). The operation is aborted\n+however if the switch leads to loss of local changes, unless told\n+otherwise.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new-branch>::\n+\tName for the new branch.\n+\n+<start-point>::\n+\tThe name of a commit at which to switch to before creating a\n+\tnew branch or detach from.\n++\n+You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+branch/commit switched to \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `\"@{-1}`.\n++\n+As a special case, you may use `\"A...B\"` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new-branch>::\n+--create <new-branch>::\n+\tCreate a new branch named <new-branch> starting at\n+\t<start-point> before switching to the branch. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-C <new-branch>::\n+--force-create <new-branch>::\n+\tSimilar to `--create` except that if <new-branch> already\n+\texists, it will be reset to <start-point>. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch -f <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+-g::\n+--guess::\n+\tIf <branch> is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it <remote>) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+-f::\n+--force::\n+\tProceed even if the index or the working tree differs from\n+\tHEAD. Both the index and working tree are restored to match\n+\tthe switching target. This is used to throw away local\n+\tchanges.\n+\n+--[no-]progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use \"hack\" as\n+the local branch when branching off of \"origin/hack\" (or\n+\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\tbranch.autoSetupMerge configuration variable is true.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as --merge option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\tmerge.conflictStyle configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+--orphan <new-branch>::\n+\tCreate a new 'orphan' branch, named <new-branch>, started from\n+\t<start-point> and switch to it. See explanation of the same\n+\toption in linkgit:git-checkout[1] for details.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--[no-]recurse-submodules::\n+\tUsing --recurse-submodules will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name, use `-g`:\n+\n+------------\n+$ git switch -g new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit \"HEAD~3\" for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex b8392fc330..5731caa4c6 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 959044347e..e257a7810a 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -161,12 +161,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -402,7 +403,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 6e8d017e8e..0e5b29390c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -789,6 +789,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8ea1349cce..2ac53b4302 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1396,33 +1401,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1555,3 +1552,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 0ce0e13f0f..31b8e8d6a1 100644\n--- a/git.c\n+++ b/git.c\n@@ -558,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368795","messageId":"20190208090401.14793-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 09/19] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:51Z","receivedAt":"2019-02-08T09:05:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout\nout. Move -b/-B to cmd_checkout() and new -c/-C with the same\nfunctionality in cmd_switch_branch()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2ac53b4302..f6de232922 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1353,14 +1353,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1556,15 +1552,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1577,14 +1580,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368796","messageId":"20190208090401.14793-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 10/19] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:52Z","receivedAt":"2019-02-08T09:05:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f6de232922..f7d8fa7caa 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1357,7 +1357,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1558,6 +1557,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368797","messageId":"20190208090401.14793-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 11/19] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:53Z","receivedAt":"2019-02-08T09:05:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f7d8fa7caa..4822d5c5d7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -52,6 +52,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1162,10 +1163,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1199,11 +1206,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1248,7 +1256,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1564,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1591,6 +1600,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368798","messageId":"20190208090401.14793-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 12/19] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:54Z","receivedAt":"2019-02-08T09:05:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4822d5c5d7..86e524f7c1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1319,6 +1320,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"missing branch or commit argument\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1572,6 +1579,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1601,6 +1609,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368799","messageId":"20190208090401.14793-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 13/19] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:55Z","receivedAt":"2019-02-08T09:05:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 +++++++++++\n 1 file changed, 11 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 86e524f7c1..d6c968f5f3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1326,6 +1327,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"missing branch or commit argument\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1581,6 +1590,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1610,6 +1620,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368800","messageId":"20190208090401.14793-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 14/19] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:56Z","receivedAt":"2019-02-08T09:05:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d6c968f5f3..a5284391a9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1381,7 +1381,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368801","messageId":"20190208090401.14793-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:57Z","receivedAt":"2019-02-08T09:05:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Similar to automatic detach, this behavior could be confusing because\nit can sometimes create a new branch without a user asking it to,\nespecially when the user is still not aware about this feature.\n\nIn the future, perhaps we could have a config key to disable these\nsafety nets and let 'switch' do automatic detach or dwim\nagain. But that will be opt-in after the user knows what is what. For\nnow give a short option if you want to use it often.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 37 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++--------\n 2 files changed, 30 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 31c6cbef19..bcb7822c27 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t<branch>.\n +\n If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it <remote>) with a matching name and\n+--no-guess is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \tbranch.autoSetupMerge configuration variable is true.\n \n+--[no-]guess::\n+\tIf <branch> is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it <remote>) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -284,10 +295,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach the\n \tsubmodules HEAD.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --[no-]overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a5284391a9..a49ab35b7d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -52,7 +52,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \n@@ -1388,8 +1388,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1426,7 +1424,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1441,7 +1438,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1501,7 +1497,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1582,12 +1578,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1612,12 +1610,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL('g', \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368802","messageId":"20190208090401.14793-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 16/19] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:58Z","receivedAt":"2019-02-08T09:05:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 127 +++---------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 9 insertions(+), 140 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex fc211eca58..2fe24049fe 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -17,14 +17,6 @@ and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n \n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\n-\n checkout.overlayMode::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a49ab35b7d..21a3fe3fb8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -25,8 +25,6 @@\n #include \"submodule.h\"\n #include \"advice.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -55,11 +53,7 @@ struct checkout_opts {\n \tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -556,104 +550,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1011,6 +907,8 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n+\n \tmemset(&old_branch_info, 0, sizeof(old_branch_info));\n \told_branch_info.path = path_to_free = resolve_refdup(\"HEAD\", 0, &rev, &flag);\n \tif (old_branch_info.path)\n@@ -1027,16 +925,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1058,11 +952,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n \tstruct checkout_opts *opts = cb;\n \n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"checkout.overlaymode\")) {\n \t\topts->overlay_mode = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1587,6 +1476,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1620,6 +1510,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368803","messageId":"20190208090401.14793-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 17/19] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:03:59Z","receivedAt":"2019-02-08T09:05:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 87 insertions(+)\n create mode 100755 t/t2060-switch.sh\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..1e1e834c1b\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,87 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch to a commit' '\n+\ttest_must_fail git switch master^{commit}\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\ttest_cmp_rev master^ refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\tgit switch -C temp &&\n+\ttest_cmp_rev master refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --orphan new-orphan master^ &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_commit expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch foo &&\n+\tgit switch --guess foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368804","messageId":"20190208090401.14793-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 18/19] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:04:00Z","receivedAt":"2019-02-08T09:06:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 499e56f83d..891abb72d7 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2126,6 +2126,32 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if ---guess was specified to enable DWIM mode\n+\t\tlocal track_opt= only_local_ref=n\n+\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\telif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y ]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368805","messageId":"20190208090401.14793-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v2 19/19] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-08T09:04:01Z","receivedAt":"2019-02-08T09:06:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt           | 12 +++---\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 ++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 18 ++++-----\n Documentation/giteveryday.txt          | 24 +++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 56 +++++++++++++-------------\n advice.c                               | 11 +++--\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             | 16 ++++----\n 17 files changed, 100 insertions(+), 95 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex bf5316ffa9..3cf6153415 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -293,7 +293,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -318,9 +318,9 @@ is currently checked out) does not have all commits from the test branch.\n NOTES\n -----\n \n-If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n-a branch and check it out with a single command.\n+If you are creating a branch that you want to switch to immediately,\n+it is easier to use the \"git switch\" command with its `-c` option to\n+do the same thing with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n serve four related but different purposes:\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 27304428a1..3d42853529 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4dd5853d6e..420777c10b 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 9f69ae8b69..020ba838c9 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..6bd0f192cc 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git reset --hard master^2\n-$ git checkout master\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..38015026bb 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch pu && git reset --hard next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..94799faa2b 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n-accept an arbitrary commit; for example, you can check out the commit\n-referenced by a tag:\n+The `git switch` command normally expects a branch head, but will also\n+accept an arbitrary commit when invoked with --detach; for example,\n+you can check out the commit referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..457c1a255e 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: switching to '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by switching back to a branch.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/sha1-name.c b/sha1-name.c\nindex a656481c6a..e5f0832995 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -745,7 +745,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..f85dbd6740 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -195,16 +195,16 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit three\n \tEOF\n@@ -271,16 +271,16 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit... three\n \tEOF\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368836","messageId":"20190208141915.GX10587@szeder.dev","threadId":"50360","inReplyTo":"20190208090401.14793-19-pclouds@gmail.com","subject":"Re: [PATCH v2 18/19] completion: support switch","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-02-08T14:19:15Z","receivedAt":"2019-02-08T14:19:22Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Fri, Feb 08, 2019 at 04:04:00PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> Completion support for --guess could be made better. If no --detach is\n> given, we should only provide a list of refs/heads/* and dwim ones,\n> not the entire ref space. But I still can't penetrate that\n> __git_refs() function yet.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++\n>  1 file changed, 26 insertions(+)\n> \n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 499e56f83d..891abb72d7 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -2126,6 +2126,32 @@ _git_status ()\n>  \t__git_complete_index_file \"$complete_opt\"\n>  }\n>  \n> +_git_switch ()\n> +{\n> +\tcase \"$cur\" in\n> +\t--conflict=*)\n> +\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n> +\t\t;;\n> +\t--*)\n> +\t\t__gitcomp_builtin switch\n> +\t\t;;\n> +\t*)\n> +\t\t# check if ---guess was specified to enable DWIM mode\n\nNit: s/---/--/\n\n> +\t\tlocal track_opt= only_local_ref=n\n> +\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n> +\t\t\ttrack_opt='--track'\n> +\t\telif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n> +\t\t\tonly_local_ref=y\n> +\t\tfi\n\nCould these two options be used together?  I think they could.  If\nthat's the case, then the two conditions shouldn't be chained with\nelif, but should be two separate if statements (even eliminating\n$only_local_ref, while at it?).  If that's not the case, then the two\n__git_find_on_cmdline() calls could be combined into one, and a case\nstatement could act according the option found, sparing one of the\nsubshells from the two.\n\n> +\t\tif [ $only_local_ref = y ]; then\n> +\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n> +\t\telse\n> +\t\t\t__git_complete_refs $track_opt\n> +\t\tfi\n> +\t\t;;\n> +\tesac\n> +}\n> +\n>  __git_config_get_set_variables ()\n>  {\n>  \tlocal prevword word config_file= c=$cword\n> -- \n> 2.20.1.682.gd5861c6d90\n> \n"},{"id":"368910","messageId":"CACsJy8Abn7xo6THsOrbt7NJ5ZohgTaWf+hKt+oVCFpHxE0r9KA@mail.gmail.com","threadId":"50360","inReplyTo":"20190208141915.GX10587@szeder.dev","subject":"Re: [PATCH v2 18/19] completion: support switch","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-09T05:30:39Z","receivedAt":"2019-02-09T05:31:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 8, 2019 at 9:19 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:\n>\n> On Fri, Feb 08, 2019 at 04:04:00PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > Completion support for --guess could be made better. If no --detach is\n> > given, we should only provide a list of refs/heads/* and dwim ones,\n> > not the entire ref space. But I still can't penetrate that\n> > __git_refs() function yet.\n> >\n> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> > ---\n> >  contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++\n> >  1 file changed, 26 insertions(+)\n> >\n> > diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> > index 499e56f83d..891abb72d7 100644\n> > --- a/contrib/completion/git-completion.bash\n> > +++ b/contrib/completion/git-completion.bash\n> > @@ -2126,6 +2126,32 @@ _git_status ()\n> >       __git_complete_index_file \"$complete_opt\"\n> >  }\n> >\n> > +_git_switch ()\n> > +{\n> > +     case \"$cur\" in\n> > +     --conflict=*)\n> > +             __gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n> > +             ;;\n> > +     --*)\n> > +             __gitcomp_builtin switch\n> > +             ;;\n> > +     *)\n> > +             # check if ---guess was specified to enable DWIM mode\n>\n> Nit: s/---/--/\n>\n> > +             local track_opt= only_local_ref=n\n> > +             if [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n> > +                     track_opt='--track'\n> > +             elif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n> > +                     only_local_ref=y\n> > +             fi\n>\n> Could these two options be used together?  I think they could.\n\nIt does not make much sense when dwim is active since you'll be\ncreating a new branch then detach from it. But yeah when you give a\nreal branch, no dwim, no new branch created, then \"git switch -dg\"\nshould work. Will fix.\n\n> If\n> that's the case, then the two conditions shouldn't be chained with\n> elif, but should be two separate if statements (even eliminating\n> $only_local_ref, while at it?).  If that's not the case, then the two\n> __git_find_on_cmdline() calls could be combined into one, and a case\n> statement could act according the option found, sparing one of the\n> subshells from the two.\n>\n> > +             if [ $only_local_ref = y ]; then\n> > +                     __gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n> > +             else\n> > +                     __git_complete_refs $track_opt\n> > +             fi\n> > +             ;;\n> > +     esac\n> > +}\n> > +\n> >  __git_config_get_set_variables ()\n> >  {\n> >       local prevword word config_file= c=$cword\n> > --\n> > 2.20.1.682.gd5861c6d90\n> >\n-- \nDuy\n"},{"id":"368959","messageId":"CAPig+cQbLLR7QeFXZLtEkHUMENdq+sy=dNvZj7jUkq-7_MXUkQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-16-pclouds@gmail.com","subject":"Re: [PATCH v2 15/19] switch: no implicit dwim, use --guess to dwim","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-02-10T08:37:26Z","receivedAt":"2019-02-10T08:53:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Feb 8, 2019 at 4:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Similar to automatic detach, this behavior could be confusing because\n> it can sometimes create a new branch without a user asking it to,\n> especially when the user is still not aware about this feature.\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> @@ -31,22 +31,13 @@ branch.\n>  If <branch> is not found but there does exist a tracking branch in\n> +exactly one remote (call it <remote>) with a matching name and\n> +--no-guess is not specified, treat as equivalent to\n\nFor consistency with typesetting in the rest of this document, you\nprobably want s/--no-guess/`&`/, though probably not worth a re-roll,\nthough, if this is the only issue with v2.\n\n> @@ -182,6 +173,26 @@ explicitly give a name with `-b` in such a case.\n> +--[no-]guess::\n> +       If <branch> is not found but there does exist a tracking\n> +       branch in exactly one remote (call it <remote>) with a\n> +       matching name, treat as equivalent to\n\nMost places in this document typeset these as `<branch>` and `<remote>`.\n"},{"id":"368997","messageId":"CAPig+cTFEc9v_60xJvF13U6RtOYThfqU+vFEUbT8KCknMbhMeQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-10-pclouds@gmail.com","subject":"Re: [PATCH v2 09/19] switch: better names for -b and -B","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-02-11T04:02:50Z","receivedAt":"2019-02-11T04:03:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Feb 8, 2019 at 4:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The shortcut of these options do not make much sense when used with\n> switch. And their descriptions are also tied to checkout\n> out. [...]\n\n\"checkout out\"?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"},{"id":"369420","messageId":"xmqqva1k4qzs.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190208090401.14793-7-pclouds@gmail.com","subject":"Re: [PATCH v2 06/19] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-15T22:38:15Z","receivedAt":"2019-02-15T22:38:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> These local variables are referenced by struct option[]. This struct\n> will soon be broken down, moved away and we can't rely on local\n> variables anymore. Move these two to struct checkout_opts in\n> preparation for that.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nUp to this point all patches made sense to me.  Thanks for working\non this.\n"},{"id":"370950","messageId":"20190308095752.8574-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190208090401.14793-1-pclouds@gmail.com","subject":"[PATCH v3 00/21] Add new command \"switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:31Z","receivedAt":"2019-03-08T09:58:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 contains document and completion updates based on v2's feedback. It\nalso contains some extra git-checkout.txt updates (blame Eric for this,\nhe points out problems in git-switch.txt and makes me want to go fix\ngit-checkout.txt too).\n\nThe series is now based on 'master' (yay!)\n\nNguyễn Thái Ngọc Duy (21):\n  git-checkout.txt: spell out --no-option\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  git-checkout.txt: fix monospace typeset\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command 'switch'\n  switch: better names for -b and -B\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: no implicit dwim, use --guess to dwim\n  switch: no worktree status unless real branch switch happens\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |  12 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                | 119 +++--\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   5 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt                  | 259 ++++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  19 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/gitworkflows.txt                |   3 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  56 +--\n Makefile                                      |   1 +\n advice.c                                      |  11 +-\n builtin.h                                     |   1 +\n builtin/checkout.c                            | 471 +++++++++---------\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  27 +\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   1 +\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |  16 +-\n t/t2060-switch.sh                             |  87 ++++\n 39 files changed, 837 insertions(+), 415 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n\nRange-diff dựa trên v2:\n -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option\n 1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line\n 2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore\n    @@ -14,14 +14,15 @@\n      \tout anyway. In other words, the ref can be held by more than one\n      \tworktree.\n      \n    -+--[no-]overwrite-ignore::\n    ++--overwrite-ignore::\n    ++--no-overwrite-ignore::\n     +\tSilently overwrite ignored files when switching branches. This\n    -+\tis the default behavior. Use --no-overwrite-ignore to abort\n    ++\tis the default behavior. Use `--no-overwrite-ignore` to abort\n     +\tthe operation when the new branch contains ignored files.\n     +\n    - --[no-]recurse-submodules::\n    + --recurse-submodules::\n    + --no-recurse-submodules::\n      \tUsing --recurse-submodules will update the content of all initialized\n    - \tsubmodules according to the commit recorded in the superproject. If\n     \n      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\n      --- a/Documentation/git-merge.txt\n    @@ -30,9 +31,10 @@\n      \tAllow the rerere mechanism to update the index with the\n      \tresult of auto-conflict resolution if possible.\n      \n    -+--[no-]overwrite-ignore::\n    ++--overwrite-ignore::\n    ++--no-overwrite-ignore::\n     +\tSilently overwrite ignored files from the merge result. This\n    -+\tis the default behavior. Use --no-overwrite-ignore to abort.\n    ++\tis the default behavior. Use `--no-overwrite-ignore` to abort.\n     +\n      --abort::\n      \tAbort the current conflict resolution process, and\n -:  ---------- >  4:  5b165524d1 git-checkout.txt: fix monospace typeset\n 3:  1e34862ad8 =  5:  a4240a888e t: rename t2014-switch.sh to t2014-checkout-switch.sh\n 4:  aba8f41f73 =  6:  dc4a8e8933 checkout: factor out some code in parse_branchname_arg()\n 5:  f165322652 =  7:  f0d933c2ac checkout: make \"opts\" in cmd_checkout() a pointer\n 6:  75f9a38e85 =  8:  0aa541689c checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n 7:  9188718493 =  9:  84ca42bb26 checkout: split options[] array in three pieces\n 8:  b5af7d22fd ! 10:  e983c8bb2c checkout: split part of it to new command switch\n    @@ -1,6 +1,6 @@\n     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n     \n    -    checkout: split part of it to new command switch\n    +    checkout: split part of it to new command 'switch'\n     \n         \"git checkout\" doing too many things is a source of confusion for many\n         users (and it even bites old timers sometimes). To remedy that, the\n    @@ -180,7 +180,7 @@\n     ++\n     +You can use the `\"@{-N}\"` syntax to refer to the N-th last\n     +branch/commit switched to \"git switch\" or \"git checkout\"\n    -+operation. You may also specify `-` which is synonymous to `\"@{-1}`.\n    ++operation. You may also specify `-` which is synonymous to `\"@{-1}\"`.\n     ++\n     +As a special case, you may use `\"A...B\"` as a shortcut for the merge\n     +base of `A` and `B` if there is exactly one merge base. You can leave\n    @@ -188,8 +188,8 @@\n     +\n     +-c <new-branch>::\n     +--create <new-branch>::\n    -+\tCreate a new branch named <new-branch> starting at\n    -+\t<start-point> before switching to the branch. This is a\n    ++\tCreate a new branch named `<new-branch>` starting at\n    ++\t`<start-point>` before switching to the branch. This is a\n     +\tconvenient shortcut for:\n     ++\n     +------------\n    @@ -199,8 +199,8 @@\n     +\n     +-C <new-branch>::\n     +--force-create <new-branch>::\n    -+\tSimilar to `--create` except that if <new-branch> already\n    -+\texists, it will be reset to <start-point>. This is a\n    ++\tSimilar to `--create` except that if `<new-branch>` already\n    ++\texists, it will be reset to `<start-point>`. This is a\n     +\tconvenient shortcut for:\n     ++\n     +------------\n    @@ -216,8 +216,8 @@\n     +\n     +-g::\n     +--guess::\n    -+\tIf <branch> is not found but there does exist a tracking\n    -+\tbranch in exactly one remote (call it <remote>) with a\n    ++\tIf `<branch>` is not found but there does exist a tracking\n    ++\tbranch in exactly one remote (call it `<remote>`) with a\n     +\tmatching name, treat as equivalent to\n     ++\n     +------------\n    @@ -243,7 +243,8 @@\n     +\tthe switching target. This is used to throw away local\n     +\tchanges.\n     +\n    -+--[no-]progress::\n    ++--progress::\n    ++--no-progress::\n     +\tProgress status is reported on the standard error stream\n     +\tby default when it is attached to a terminal, unless `--quiet`\n     +\tis specified. This flag enables progress reporting even if not\n    @@ -292,8 +293,8 @@\n     +\t\"merge\" style, shows the original contents).\n     +\n     +--orphan <new-branch>::\n    -+\tCreate a new 'orphan' branch, named <new-branch>, started from\n    -+\t<start-point> and switch to it. See explanation of the same\n    ++\tCreate a new 'orphan' branch, named `<new-branch>`, started from\n    ++\t`<start-point>` and switch to it. See explanation of the same\n     +\toption in linkgit:git-checkout[1] for details.\n     +\n     +--ignore-other-worktrees::\n    @@ -302,7 +303,8 @@\n     +\tthe ref out anyway. In other words, the ref can be held by\n     +\tmore than one worktree.\n     +\n    -+--[no-]recurse-submodules::\n    ++--recurse-submodules::\n    ++--no-recurse-submodules::\n     +\tUsing --recurse-submodules will update the content of all initialized\n     +\tsubmodules according to the commit recorded in the superproject. If\n     +\tlocal modifications in a submodule would be overwritten the checkout\n 9:  5be400099c ! 11:  5491932cc8 switch: better names for -b and -B\n    @@ -3,9 +3,9 @@\n         switch: better names for -b and -B\n     \n         The shortcut of these options do not make much sense when used with\n    -    switch. And their descriptions are also tied to checkout\n    -    out. Move -b/-B to cmd_checkout() and new -c/-C with the same\n    -    functionality in cmd_switch_branch()\n    +    switch. And their descriptions are also tied to checkout. Move -b/-B\n    +    to cmd_checkout() and new -c/-C with the same functionality in\n    +    cmd_switch_branch()\n     \n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n10:  38cfc1fada = 12:  e0f1247b09 switch: remove -l\n11:  7f0abab72c = 13:  f7ff1a7df6 switch: stop accepting pathspec\n12:  386ba95007 = 14:  0750d63d38 switch: reject \"do nothing\" case\n13:  e488c7d8ca = 15:  bcd843146d switch: only allow explicit detached HEAD\n14:  9cd5f63c01 = 16:  d5cc55525d switch: add short option for --detach\n15:  fc31b3d4f3 ! 17:  4a27400a58 switch: no implicit dwim, use --guess to dwim\n    @@ -15,13 +15,13 @@\n      --- a/Documentation/git-checkout.txt\n      +++ b/Documentation/git-checkout.txt\n     @@\n    - \t<branch>.\n    + \t`<branch>`.\n      +\n    - If <branch> is not found but there does exist a tracking branch in\n    --exactly one remote (call it <remote>) with a matching name, treat as\n    + If `<branch>` is not found but there does exist a tracking branch in\n    +-exactly one remote (call it `<remote>`) with a matching name, treat as\n     -equivalent to\n    -+exactly one remote (call it <remote>) with a matching name and\n    -+--no-guess is not specified, treat as equivalent to\n    ++exactly one remote (call it `<remote>`) with a matching name and\n    ++`--no-guess` is not specified, treat as equivalent to\n      +\n      ------------\n      $ git checkout -b <branch> --track <remote>/<branch>\n    @@ -36,16 +36,17 @@\n     -'origin' remote. See also `checkout.defaultRemote` in\n     -linkgit:git-config[1].\n     -+\n    - You could omit <branch>, in which case the command degenerates to\n    + You could omit `<branch>`, in which case the command degenerates to\n      \"check out the current branch\", which is a glorified no-op with\n      rather expensive side-effects to show only the tracking information,\n     @@\n      \tDo not set up \"upstream\" configuration, even if the\n      \tbranch.autoSetupMerge configuration variable is true.\n      \n    -+--[no-]guess::\n    -+\tIf <branch> is not found but there does exist a tracking\n    -+\tbranch in exactly one remote (call it <remote>) with a\n    ++--guess::\n    ++--no-guess::\n    ++\tIf `<branch>` is not found but there does exist a tracking\n    ++\tbranch in exactly one remote (call it `<remote>`) with a\n     +\tmatching name, treat as equivalent to\n     ++\n     +------------\n    @@ -74,9 +75,9 @@\n     -\tDo not attempt to create a branch if a remote tracking branch\n     -\tof the same name exists.\n     -\n    - --[no-]overlay::\n    + --overlay::\n    + --no-overlay::\n      \tIn the default overlay mode, `git checkout` never\n    - \tremoves files from the index or the working tree.  When\n     \n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n16:  761f953b40 = 18:  f7dd33abe6 switch: no worktree status unless real branch switch happens\n17:  206f1bda79 = 19:  5b182abea8 t: add tests for switch\n18:  3cfb2913eb ! 20:  1575064fe6 completion: support switch\n    @@ -28,10 +28,11 @@\n     +\t\tlocal track_opt= only_local_ref=n\n     +\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n     +\t\t\ttrack_opt='--track'\n    -+\t\telif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n    ++\t\tfi\n    ++\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n     +\t\t\tonly_local_ref=y\n     +\t\tfi\n    -+\t\tif [ $only_local_ref = y ]; then\n    ++\t\tif [ $only_local_ref = y -a -n \"$track_opt\"]; then\n     +\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n     +\t\telse\n     +\t\t\t__git_complete_refs $track_opt\n19:  2d018878bd ! 21:  8ed01ad0c5 doc: promote \"git switch\"\n    @@ -317,9 +317,9 @@\n      \n      ------------\n     -$ git checkout mybranch\n    -+$ git switch mybranch\n    - $ git reset --hard master^2\n    +-$ git reset --hard master^2\n     -$ git checkout master\n    ++$ git switch -C mybranch master^2\n     +$ git switch master\n      $ git reset --hard master^\n      ------------\n    @@ -400,7 +400,7 @@\n     -$ git checkout pu && git reset --hard next <7>\n     +$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n     +$ git switch topic/one && git rebase master <6>\n    -+$ git switch pu && git reset --hard next <7>\n    ++$ git switch -C pu next <7>\n      $ git merge topic/one topic/two && git merge hold/linus <8>\n     -$ git checkout maint\n     +$ git switch maint\n    @@ -430,6 +430,20 @@\n      \n      Check that the change you made is no longer visible, since it was\n     \n    + diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt\n    + --- a/Documentation/gitworkflows.txt\n    + +++ b/Documentation/gitworkflows.txt\n    +@@\n    + .Rewind and rebuild next\n    + [caption=\"Recipe: \"]\n    + =====================================\n    +-* `git checkout next`\n    +-* `git reset --hard master`\n    ++* `git switch -C next master`\n    + * `git merge ai/topic_in_next1`\n    + * `git merge ai/topic_in_next2`\n    + * ...\n    +\n      diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\n      --- a/Documentation/revisions.txt\n      +++ b/Documentation/revisions.txt\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370951","messageId":"20190308095752.8574-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 01/21] git-checkout.txt: spell out --no-option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:32Z","receivedAt":"2019-03-08T09:58:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's easier to search for and also less cryptic.\n---\n Documentation/git-checkout.txt | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f179b43732..99c8c0dc0f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -118,7 +118,8 @@ OPTIONS\n --quiet::\n \tQuiet, suppress feedback messages.\n \n---[no-]progress::\n+--progress::\n+--no-progress::\n \tProgress status is reported on the standard error stream\n \tby default when it is attached to a terminal, unless `--quiet`\n \tis specified. This flag enables progress reporting even if not\n@@ -262,7 +263,7 @@ edits from your current working tree. See the ``Interactive Mode''\n section of linkgit:git-add[1] to learn how to operate the `--patch` mode.\n +\n Note that this option uses the no overlay mode by default (see also\n-`--[no-]overlay`), and currently doesn't support overlay mode.\n+`--overlay`), and currently doesn't support overlay mode.\n \n --ignore-other-worktrees::\n \t`git checkout` refuses when the wanted ref is already checked\n@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n---[no-]recurse-submodules::\n+--recurse-submodules::\n+--no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also\n \tDo not attempt to create a branch if a remote tracking branch\n \tof the same name exists.\n \n---[no-]overlay::\n+--overlay::\n+--no-overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370952","messageId":"20190308095752.8574-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 02/21] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:33Z","receivedAt":"2019-03-08T09:58:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 99c8c0dc0f..28817cfa41 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370953","messageId":"20190308095752.8574-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 03/21] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:34Z","receivedAt":"2019-03-08T09:58:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n---\n Documentation/git-checkout.txt | 6 ++++++\n Documentation/git-merge.txt    | 5 +++++\n 2 files changed, 11 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 28817cfa41..5280d1f9ed 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --recurse-submodules::\n --no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..6a9163d8fe 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,11 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370954","messageId":"20190308095752.8574-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 04/21] git-checkout.txt: fix monospace typeset","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:35Z","receivedAt":"2019-03-08T09:58:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"---\n Documentation/git-checkout.txt | 60 +++++++++++++++++-----------------\n 1 file changed, 30 insertions(+), 30 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 5280d1f9ed..1b9d689933 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current\n branch.\n \n 'git checkout' [<branch>]::\n-\tTo prepare for working on <branch>, switch to it by updating\n+\tTo prepare for working on `<branch>`, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n \tworking tree are kept, so that they can be committed to the\n-\t<branch>.\n+\t`<branch>`.\n +\n-If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n+If `<branch>` is not found but there does exist a tracking branch in\n+exactly one remote (call it `<remote>`) with a matching name, treat as\n equivalent to\n +\n ------------\n@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the\n 'origin' remote. See also `checkout.defaultRemote` in\n linkgit:git-config[1].\n +\n-You could omit <branch>, in which case the command degenerates to\n+You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n if exists, for the current branch.\n@@ -61,7 +61,7 @@ if exists, for the current branch.\n \t`--track` without `-b` implies branch creation; see the\n \tdescription of `--track` below.\n +\n-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it\n+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it\n is reset. This is the transactional equivalent of\n +\n ------------\n@@ -75,25 +75,25 @@ successful.\n 'git checkout' --detach [<branch>]::\n 'git checkout' [--detach] <commit>::\n \n-\tPrepare to work on top of <commit>, by detaching HEAD at it\n+\tPrepare to work on top of `<commit>`, by detaching HEAD at it\n \t(see \"DETACHED HEAD\" section), and updating the index and the\n \tfiles in the working tree.  Local modifications to the files\n \tin the working tree are kept, so that the resulting working\n \ttree will be the state recorded in the commit plus the local\n \tmodifications.\n +\n-When the <commit> argument is a branch name, the `--detach` option can\n+When the `<commit>` argument is a branch name, the `--detach` option can\n be used to detach HEAD at the tip of the branch (`git checkout\n-<branch>` would check out that branch without detaching HEAD).\n+`<branch>`` would check out that branch without detaching HEAD).\n +\n-Omitting <branch> detaches HEAD at the tip of the current branch.\n+Omitting `<branch>` detaches HEAD at the tip of the current branch.\n \n 'git checkout' [<tree-ish>] [--] <pathspec>...::\n \n \tOverwrite paths in the working tree by replacing with the\n-\tcontents in the index or in the <tree-ish> (most often a\n-\tcommit).  When a <tree-ish> is given, the paths that\n-\tmatch the <pathspec> are updated both in the index and in\n+\tcontents in the index or in the `<tree-ish>` (most often a\n+\tcommit).  When a `<tree-ish>` is given, the paths that\n+\tmatch the `<pathspec>` are updated both in the index and in\n \tthe working tree.\n +\n The index may contain unmerged entries because of a previous failed merge.\n@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. \"one contributor's work on top\n of it\").\n \n -b <new_branch>::\n-\tCreate a new branch named <new_branch> and start it at\n-\t<start_point>; see linkgit:git-branch[1] for details.\n+\tCreate a new branch named `<new_branch>` and start it at\n+\t`<start_point>`; see linkgit:git-branch[1] for details.\n \n -B <new_branch>::\n-\tCreates the branch <new_branch> and start it at <start_point>;\n-\tif it already exists, then reset it to <start_point>. This is\n+\tCreates the branch `<new_branch>` and start it at `<start_point>`;\n+\tif it already exists, then reset it to `<start_point>`. This is\n \tequivalent to running \"git branch\" with \"-f\"; see\n \tlinkgit:git-branch[1] for details.\n \n@@ -191,19 +191,19 @@ explicitly give a name with `-b` in such a case.\n \tRather than checking out a branch to work on it, check out a\n \tcommit for inspection and discardable experiments.\n \tThis is the default behavior of \"git checkout <commit>\" when\n-\t<commit> is not a branch name.  See the \"DETACHED HEAD\" section\n+\t`<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n \tbelow for details.\n \n --orphan <new_branch>::\n-\tCreate a new 'orphan' branch, named <new_branch>, started from\n-\t<start_point> and switch to it.  The first commit made on this\n+\tCreate a new 'orphan' branch, named `<new_branch>`, started from\n+\t`<start_point>` and switch to it.  The first commit made on this\n \tnew branch will have no parents and it will be the root of a new\n \thistory totally disconnected from all the other branches and\n \tcommits.\n +\n The index and the working tree are adjusted as if you had previously run\n \"git checkout <start_point>\".  This allows you to start a new history\n-that records a set of paths similar to <start_point> by easily running\n+that records a set of paths similar to `<start_point>` by easily running\n \"git commit -a\" to make the root commit.\n +\n This can be useful when you want to publish the tree from a commit\n@@ -213,7 +213,7 @@ whose full history contains proprietary or otherwise encumbered bits of\n code.\n +\n If you want to start a disconnected history that records a set of paths\n-that is totally different from the one of <start_point>, then you should\n+that is totally different from the one of `<start_point>`, then you should\n clear the index and the working tree right after creating the orphan\n branch by running \"git rm -rf .\" from the top level of the working tree.\n Afterwards you will be ready to prepare your new files, repopulating the\n@@ -221,9 +221,9 @@ working tree, by copying them from elsewhere, extracting a tarball, etc.\n \n --ignore-skip-worktree-bits::\n \tIn sparse checkout mode, `git checkout -- <paths>` would\n-\tupdate only entries matched by <paths> and sparse patterns\n+\tupdate only entries matched by `<paths>` and sparse patterns\n \tin $GIT_DIR/info/sparse-checkout. This option ignores\n-\tthe sparse patterns and adds back any files in <paths>.\n+\tthe sparse patterns and adds back any files in `<paths>`.\n \n -m::\n --merge::\n@@ -254,9 +254,9 @@ the conflicted merge in the specified paths.\n -p::\n --patch::\n \tInteractively select hunks in the difference between the\n-\t<tree-ish> (or the index, if unspecified) and the working\n+\t`<tree-ish>` (or the index, if unspecified) and the working\n \ttree.  The chosen hunks are then applied in reverse to the\n-\tworking tree (and if a <tree-ish> was specified, the index).\n+\tworking tree (and if a `<tree-ish>` was specified, the index).\n +\n This means that you can use `git checkout -p` to selectively discard\n edits from your current working tree. See the ``Interactive Mode''\n@@ -279,10 +279,10 @@ Note that this option uses the no overlay mode by default (see also\n \n --recurse-submodules::\n --no-recurse-submodules::\n-\tUsing --recurse-submodules will update the content of all initialized\n+\tUsing `--recurse-submodules` will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n \tis used, the work trees of submodules will not be updated.\n \tJust like linkgit:git-submodule[1], this will detach the\n \tsubmodules HEAD.\n@@ -296,8 +296,8 @@ Note that this option uses the no overlay mode by default (see also\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-\tworking tree, but not in <tree-ish> are removed, to make them\n-\tmatch <tree-ish> exactly.\n+\tworking tree, but not in `<tree-ish>` are removed, to make them\n+\tmatch `<tree-ish>` exactly.\n \n <branch>::\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370955","messageId":"20190308095752.8574-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 05/21] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:36Z","receivedAt":"2019-03-08T09:58:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370956","messageId":"20190308095752.8574-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 06/21] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:37Z","receivedAt":"2019-03-08T09:58:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e6037b296..cbdcb1417f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370957","messageId":"20190308095752.8574-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 07/21] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:38Z","receivedAt":"2019-03-08T09:58:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cbdcb1417f..baefe3c942 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370958","messageId":"20190308095752.8574-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 08/21] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:39Z","receivedAt":"2019-03-08T09:58:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex baefe3c942..4b43433941 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -47,6 +47,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -58,6 +60,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370959","messageId":"20190308095752.8574-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 09/21] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:40Z","receivedAt":"2019-03-08T09:58:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  1 +\n 3 files changed, 77 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4b43433941..7d23083282 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 2733393546..caaeed896f 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..9a90c332a5 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n+struct option *parse_options_dup(const struct option *a);\n struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370960","messageId":"20190308095752.8574-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:41Z","receivedAt":"2019-03-08T09:58:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and\nsomething-to-checkout-paths. The good old \"git checkout\" command is\nstill here and will be until all (or most of users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n---\n .gitignore                        |   1 +\n Documentation/config/advice.txt   |  13 +-\n Documentation/config/branch.txt   |   4 +-\n Documentation/config/checkout.txt |   9 +-\n Documentation/config/diff.txt     |   3 +-\n Documentation/git-checkout.txt    |   4 +\n Documentation/git-switch.txt      | 259 ++++++++++++++++++++++++++++++\n Documentation/gitattributes.txt   |   3 +-\n Documentation/githooks.txt        |   8 +-\n Makefile                          |   1 +\n builtin.h                         |   1 +\n builtin/checkout.c                |  60 +++++--\n command-list.txt                  |   1 +\n git.c                             |   1 +\n 14 files changed, 341 insertions(+), 27 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex 7374587f9d..c687b92b1c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex c4118fa196..d6872ffa83 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 1b9d689933..ac355dc3f3 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -560,6 +560,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..ff60ba69fb\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,259 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--guess] <branch>\n+'git switch' [<options>] --detach [<start-point>]\n+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch. The working tree and the index are\n+updated to match the branch. All new commits will be added to the tip\n+of this branch.\n+\n+Optionally a new branch could be created with either `-c` or `-C`, or\n+detach the working tree from any branch with `--detach`, along with\n+switching.\n+\n+Switching branches does not require a clean index and working tree\n+(i.e. no differences compared to 'HEAD'). The operation is aborted\n+however if the switch leads to loss of local changes, unless told\n+otherwise.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new-branch>::\n+\tName for the new branch.\n+\n+<start-point>::\n+\tThe name of a commit at which to switch to before creating a\n+\tnew branch or detach from.\n++\n+You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+branch/commit switched to \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `\"@{-1}\"`.\n++\n+As a special case, you may use `\"A...B\"` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new-branch>::\n+--create <new-branch>::\n+\tCreate a new branch named `<new-branch>` starting at\n+\t`<start-point>` before switching to the branch. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-C <new-branch>::\n+--force-create <new-branch>::\n+\tSimilar to `--create` except that if `<new-branch>` already\n+\texists, it will be reset to `<start-point>`. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch -f <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+-g::\n+--guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+-f::\n+--force::\n+\tProceed even if the index or the working tree differs from\n+\tHEAD. Both the index and working tree are restored to match\n+\tthe switching target. This is used to throw away local\n+\tchanges.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use \"hack\" as\n+the local branch when branching off of \"origin/hack\" (or\n+\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\tbranch.autoSetupMerge configuration variable is true.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as --merge option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\tmerge.conflictStyle configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+--orphan <new-branch>::\n+\tCreate a new 'orphan' branch, named `<new-branch>`, started from\n+\t`<start-point>` and switch to it. See explanation of the same\n+\toption in linkgit:git-checkout[1] for details.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--recurse-submodules::\n+--no-recurse-submodules::\n+\tUsing --recurse-submodules will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name, use `-g`:\n+\n+------------\n+$ git switch -g new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit \"HEAD~3\" for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 9b41f81c06..cd0f9fa507 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 5bf653c111..8ff72f0613 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -406,7 +407,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 148668368b..8e91db73ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 7d23083282..1eff10dbef 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 2014aab6b8..39582cf511 100644\n--- a/git.c\n+++ b/git.c\n@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370961","messageId":"20190308095752.8574-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 11/21] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:42Z","receivedAt":"2019-03-08T09:58:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout. Move -b/-B\nto cmd_checkout() and new -c/-C with the same functionality in\ncmd_switch_branch()\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1eff10dbef..4c3f0f6ac7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370962","messageId":"20190308095752.8574-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 12/21] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:43Z","receivedAt":"2019-03-08T09:59:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4c3f0f6ac7..a731f983c4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1372,7 +1372,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1573,6 +1572,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370963","messageId":"20190308095752.8574-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 13/21] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:44Z","receivedAt":"2019-03-08T09:59:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a731f983c4..1b1181b220 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1175,10 +1176,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1212,11 +1219,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1261,7 +1269,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1579,6 +1587,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1606,6 +1615,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370964","messageId":"20190308095752.8574-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 14/21] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:45Z","receivedAt":"2019-03-08T09:59:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1b1181b220..f9f7ee2936 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -54,6 +54,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1334,6 +1335,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"missing branch or commit argument\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1587,6 +1594,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1616,6 +1624,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370965","messageId":"20190308095752.8574-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 15/21] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:46Z","receivedAt":"2019-03-08T09:59:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n---\n builtin/checkout.c | 11 +++++++++++\n 1 file changed, 11 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f9f7ee2936..2e150f0175 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -45,6 +45,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1341,6 +1342,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"missing branch or commit argument\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1596,6 +1605,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1625,6 +1635,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370966","messageId":"20190308095752.8574-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 16/21] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:47Z","receivedAt":"2019-03-08T09:59:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e150f0175..0866aeba83 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1396,7 +1396,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370967","messageId":"20190308095752.8574-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:48Z","receivedAt":"2019-03-08T09:59:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Similar to automatic detach, this behavior could be confusing because\nit can sometimes create a new branch without a user asking it to,\nespecially when the user is still not aware about this feature.\n\nIn the future, perhaps we could have a config key to disable these\nsafety nets and let 'switch' do automatic detach or dwim\nagain. But that will be opt-in after the user knows what is what. For\nnow give a short option if you want to use it often.\n---\n Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++-------\n 2 files changed, 31 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ac355dc3f3..2b776c1269 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t`<branch>`.\n +\n If `<branch>` is not found but there does exist a tracking branch in\n-exactly one remote (call it `<remote>`) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it `<remote>`) with a matching name and\n+`--no-guess` is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \tbranch.autoSetupMerge configuration variable is true.\n \n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach the\n \tsubmodules HEAD.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --overlay::\n --no-overlay::\n \tIn the default overlay mode, `git checkout` never\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0866aeba83..8a89df4f36 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,7 +53,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \n@@ -1403,8 +1403,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1441,7 +1439,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1456,7 +1453,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1516,7 +1512,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1597,12 +1593,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1627,12 +1625,14 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL('g', \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370968","messageId":"20190308095752.8574-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 18/21] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:49Z","receivedAt":"2019-03-08T09:59:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 134 ++----------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 8 insertions(+), 148 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex d6872ffa83..6b646813ab 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n-\n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8a89df4f36..4903359b49 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -26,8 +26,6 @@\n #include \"submodule.h\"\n #include \"advice.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -56,11 +54,7 @@ struct checkout_opts {\n \tint dwim_new_local_branch;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -564,112 +558,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\t /*\n-\t  * Do the merge if this is the initial checkout. We cannot use\n-\t  * is_cache_unborn() here because the index hasn't been loaded yet\n-\t  * so cache_nr and timestamp.sec are always zero.\n-\t  */\n-\tif (!file_exists(get_index_file()))\n-\t\treturn 0;\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1027,6 +915,7 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n \n \ttrace2_cmd_mode(\"branch\");\n \n@@ -1046,16 +935,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1075,11 +960,6 @@ static int switch_branches(const struct checkout_opts *opts,\n \n static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"diff.ignoresubmodules\")) {\n \t\tstruct checkout_opts *opts = cb;\n \t\thandle_ignore_submodules_arg(&opts->diff_options, value);\n@@ -1602,6 +1482,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1635,6 +1516,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370969","messageId":"20190308095752.8574-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 19/21] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:50Z","receivedAt":"2019-03-08T09:59:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"---\n t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 87 insertions(+)\n create mode 100755 t/t2060-switch.sh\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..1e1e834c1b\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,87 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch to a commit' '\n+\ttest_must_fail git switch master^{commit}\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\ttest_cmp_rev master^ refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\tgit switch -C temp &&\n+\ttest_cmp_rev master refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --orphan new-orphan master^ &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_commit expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch foo &&\n+\tgit switch --guess foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370970","messageId":"20190308095752.8574-21-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 20/21] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:51Z","receivedAt":"2019-03-08T09:59:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n---\n contrib/completion/git-completion.bash | 27 ++++++++++++++++++++++++++\n 1 file changed, 27 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 976e4a6548..7fcf28d437 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2158,6 +2158,33 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if ---guess was specified to enable DWIM mode\n+\t\tlocal track_opt= only_local_ref=n\n+\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\tfi\n+\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y -a -n \"$track_opt\"]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"370971","messageId":"20190308095752.8574-22-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v3 21/21] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-08T09:57:52Z","receivedAt":"2019-03-08T09:59:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n---\n Documentation/git-branch.txt           | 12 +++---\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 ++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 19 +++++----\n Documentation/giteveryday.txt          | 24 +++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/gitworkflows.txt         |  3 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 56 +++++++++++++-------------\n advice.c                               | 11 +++--\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             | 16 ++++----\n 18 files changed, 101 insertions(+), 98 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0cd87ddeff..1e2d89b174 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -297,7 +297,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>\n NOTES\n -----\n \n-If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n-a branch and check it out with a single command.\n+If you are creating a branch that you want to switch to immediately,\n+it is easier to use the \"git switch\" command with its `-c` option to\n+do the same thing with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n serve four related but different purposes:\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..0a24a5679e 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 5629ba4c5d..cb6fc166e2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 132f8e55f6..cbf901efb4 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..f880d21dfb 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n-$ git reset --hard master^2\n-$ git checkout master\n+$ git switch -C mybranch master^2\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..ad455f3e39 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch -C pu next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt\nindex ca11c7bdaf..abc0dc6bc7 100644\n--- a/Documentation/gitworkflows.txt\n+++ b/Documentation/gitworkflows.txt\n@@ -301,8 +301,7 @@ topics on 'next':\n .Rewind and rebuild next\n [caption=\"Recipe: \"]\n =====================================\n-* `git checkout next`\n-* `git reset --hard master`\n+* `git switch -C next master`\n * `git merge ai/topic_in_next1`\n * `git merge ai/topic_in_next2`\n * ...\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..94799faa2b 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n-accept an arbitrary commit; for example, you can check out the commit\n-referenced by a tag:\n+The `git switch` command normally expects a branch head, but will also\n+accept an arbitrary commit when invoked with --detach; for example,\n+you can check out the commit referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..457c1a255e 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,16 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: switching to '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by switching back to a branch.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..da0518c8e3 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..f85dbd6740 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -195,16 +195,16 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit three\n \tEOF\n@@ -271,16 +271,16 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tHEAD is now at \\$commit... three\n \tEOF\n-- \n2.21.0.rc1.337.gdf7f8d0522\n\n"},{"id":"371003","messageId":"d0f69dda-f677-45b8-f70e-971df27ce224@ramsayjones.plus.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"Re: [PATCH v3 00/21] Add new command \"switch\"","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2019-03-08T17:48:12Z","receivedAt":"2019-03-08T17:48:18Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n[snip]\n> Range-diff dựa trên v2:\n>  -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option\n>  1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line\n>  2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore\n>     @@ -14,14 +14,15 @@\n>       \tout anyway. In other words, the ref can be held by more than one\n>       \tworktree.\n>       \n>     -+--[no-]overwrite-ignore::\n>     ++--overwrite-ignore::\n>     ++--no-overwrite-ignore::\n\nJust curious, but why? Is '--[no-]overwrite-ignore' thought to\nbe harder to read? What about the rest of the man-pages?\n\nATB,\nRamsay Jones\n\n"},{"id":"371023","messageId":"CACsJy8CuTCOjeOJ0B9Q+xR5LcSY_mwns1gwtApk0TDUm5YZEtQ@mail.gmail.com","threadId":"50360","inReplyTo":"d0f69dda-f677-45b8-f70e-971df27ce224@ramsayjones.plus.com","subject":"Re: [PATCH v3 00/21] Add new command \"switch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-09T11:56:21Z","receivedAt":"2019-03-09T11:56:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Mar 9, 2019 at 12:49 AM Ramsay Jones\n<ramsay@ramsayjones.plus.com> wrote:\n>\n>\n>\n> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> [snip]\n> > Range-diff dựa trên v2:\n> >  -:  ---------- >  1:  949f3dd4fd git-checkout.txt: spell out --no-option\n> >  1:  8358b9ca36 =  2:  1ddbbae3e2 git-checkout.txt: fix one syntax line\n> >  2:  1686ccbf8d !  3:  b0cb2372db doc: document --overwrite-ignore\n> >     @@ -14,14 +14,15 @@\n> >               out anyway. In other words, the ref can be held by more than one\n> >               worktree.\n> >\n> >     -+--[no-]overwrite-ignore::\n> >     ++--overwrite-ignore::\n> >     ++--no-overwrite-ignore::\n>\n> Just curious, but why? Is '--[no-]overwrite-ignore' thought to\n> be harder to read? What about the rest of the man-pages?\n\nIt's also easier to search. If I remember correctly I did search for\nthe rest of man pages and the --no- wins over --[no-]\n-- \nDuy\n"},{"id":"371027","messageId":"20190309123518.2193053-1-martin.agren@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-5-pclouds@gmail.com","subject":"Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-03-09T12:35:18Z","receivedAt":"2019-03-09T12:35:49Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Fri, 8 Mar 2019 at 10:58, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>  Documentation/git-checkout.txt | 60 +++++++++++++++++-----------------\n>  1 file changed, 30 insertions(+), 30 deletions(-)\n>\n>  'git checkout' [<branch>]::\n> -       To prepare for working on <branch>, switch to it by updating\n> +       To prepare for working on `<branch>`, switch to it by updating\n>         the index and the files in the working tree, and by pointing\n>         HEAD at the branch. Local modifications to the files in the\n\nYou could wrap HEAD in backticks as well: `HEAD`.\n\n> -       Prepare to work on top of <commit>, by detaching HEAD at it\n> +       Prepare to work on top of `<commit>`, by detaching HEAD at it\n\nLikewise.\n\n>         (see \"DETACHED HEAD\" section), and updating the index and the\n\n(but not here)\n\n>  be used to detach HEAD at the tip of the branch (`git checkout\n> -<branch>` would check out that branch without detaching HEAD).\n> +`<branch>`` would check out that branch without detaching HEAD).\n\nThis results in a nesting. I would say this was already correct. (A few\nmore `HEAD` here.)\n\n> -       Create a new branch named <new_branch> and start it at\n> -       <start_point>; see linkgit:git-branch[1] for details.\n> +       Create a new branch named `<new_branch>` and start it at\n> +       `<start_point>`; see linkgit:git-branch[1] for details.\n\nLots of changes like this. Good.\n\n>         This is the default behavior of \"git checkout <commit>\" when\n\nShould be `git checkout <commit>`?\n\n> -       <commit> is not a branch name.  See the \"DETACHED HEAD\" section\n> +       `<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n\nGood.\n\n> -that records a set of paths similar to <start_point> by easily running\n> +that records a set of paths similar to `<start_point>` by easily running\n>  \"git commit -a\" to make the root commit.\n\nAnother opportunity for backticks.\n\nI saw one change that I disagreed with and a few missed opportunities\n(IMHO). How about something like the below squashed in? (This is based\non this point in the series.) It might be worth polishing this document\nbefore copying it (or parts of it) in the next commit.\n\nMartin\n\n-- >8 --\nSubject: git-checkout.txt: add more backticks for monospacing\n\nAdd backticks where we have none, replace single quotes with backticks\nand replace double-quotes. Drop double-quotes from nested constructions\nsuch as `\"@{-1}\"`. Add a missing possessive apostrophe after the word\n\"submodules\" while at it.\n\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\n---\n Documentation/git-checkout.txt | 106 ++++++++++++++++-----------------\n 1 file changed, 53 insertions(+), 53 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 1b9d689933..af5906fbf5 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -26,7 +26,7 @@ branch.\n 'git checkout' [<branch>]::\n \tTo prepare for working on `<branch>`, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n-\tHEAD at the branch. Local modifications to the files in the\n+\t`HEAD` at the branch. Local modifications to the files in the\n \tworking tree are kept, so that they can be committed to the\n \t`<branch>`.\n +\n@@ -75,7 +75,7 @@ successful.\n 'git checkout' --detach [<branch>]::\n 'git checkout' [--detach] <commit>::\n \n-\tPrepare to work on top of `<commit>`, by detaching HEAD at it\n+\tPrepare to work on top of `<commit>`, by detaching `HEAD` at it\n \t(see \"DETACHED HEAD\" section), and updating the index and the\n \tfiles in the working tree.  Local modifications to the files\n \tin the working tree are kept, so that the resulting working\n@@ -83,10 +83,10 @@ successful.\n \tmodifications.\n +\n When the `<commit>` argument is a branch name, the `--detach` option can\n-be used to detach HEAD at the tip of the branch (`git checkout\n-`<branch>`` would check out that branch without detaching HEAD).\n+be used to detach `HEAD` at the tip of the branch (`git checkout\n+<branch>` would check out that branch without detaching `HEAD`).\n +\n-Omitting `<branch>` detaches HEAD at the tip of the current branch.\n+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.\n \n 'git checkout' [<tree-ish>] [--] <pathspec>...::\n \n@@ -128,7 +128,7 @@ OPTIONS\n -f::\n --force::\n \tWhen switching branches, proceed even if the index or the\n-\tworking tree differs from HEAD.  This is used to throw away\n+\tworking tree differs from `HEAD`.  This is used to throw away\n \tlocal changes.\n +\n When checking out paths from the index, do not fail upon unmerged\n@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be\n derived from the remote-tracking branch, by looking at the local part of\n the refspec configured for the corresponding remote, and then stripping\n the initial part up to the \"*\".\n-This would tell us to use \"hack\" as the local branch when branching\n-off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n-\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n+This would tell us to use `hack` as the local branch when branching\n+off of `origin/hack` (or `remotes/origin/hack`, or even\n+`refs/remotes/origin/hack`).  If the given name has no slash, or the above\n guessing results in an empty name, the guessing is aborted.  You can\n explicitly give a name with `-b` in such a case.\n \n --no-track::\n \tDo not set up \"upstream\" configuration, even if the\n-\tbranch.autoSetupMerge configuration variable is true.\n+\t`branch.autoSetupMerge` configuration variable is true.\n \n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n@@ -190,7 +190,7 @@ explicitly give a name with `-b` in such a case.\n --detach::\n \tRather than checking out a branch to work on it, check out a\n \tcommit for inspection and discardable experiments.\n-\tThis is the default behavior of \"git checkout <commit>\" when\n+\tThis is the default behavior of `git checkout <commit>` when\n \t`<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n \tbelow for details.\n \n@@ -202,9 +202,9 @@ explicitly give a name with `-b` in such a case.\n \tcommits.\n +\n The index and the working tree are adjusted as if you had previously run\n-\"git checkout <start_point>\".  This allows you to start a new history\n+`git checkout <start_point>`.  This allows you to start a new history\n that records a set of paths similar to `<start_point>` by easily running\n-\"git commit -a\" to make the root commit.\n+`git commit -a` to make the root commit.\n +\n This can be useful when you want to publish the tree from a commit\n without exposing its full history. You might want to do this to publish\n@@ -215,14 +215,14 @@ code.\n If you want to start a disconnected history that records a set of paths\n that is totally different from the one of `<start_point>`, then you should\n clear the index and the working tree right after creating the orphan\n-branch by running \"git rm -rf .\" from the top level of the working tree.\n+branch by running `git rm -rf .` from the top level of the working tree.\n Afterwards you will be ready to prepare your new files, repopulating the\n working tree, by copying them from elsewhere, extracting a tarball, etc.\n \n --ignore-skip-worktree-bits::\n \tIn sparse checkout mode, `git checkout -- <paths>` would\n \tupdate only entries matched by `<paths>` and sparse patterns\n-\tin $GIT_DIR/info/sparse-checkout. This option ignores\n+\tin `$GIT_DIR/info/sparse-checkout`. This option ignores\n \tthe sparse patterns and adds back any files in `<paths>`.\n \n -m::\n@@ -245,9 +245,9 @@ When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n \n --conflict=<style>::\n-\tThe same as --merge option above, but changes the way the\n+\tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n-\tmerge.conflictStyle configuration variable.  Possible values are\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n \t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n \t\"merge\" style, shows the original contents).\n \n@@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also\n \twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n \tis used, the work trees of submodules will not be updated.\n \tJust like linkgit:git-submodule[1], this will detach the\n-\tsubmodules HEAD.\n+\tsubmodules' `HEAD`.\n \n --no-guess::\n \tDo not attempt to create a branch if a remote tracking branch\n@@ -303,14 +303,14 @@ Note that this option uses the no overlay mode by default (see also\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n \twhen prepended with \"refs/heads/\", is a valid ref), then that\n \tbranch is checked out. Otherwise, if it refers to a valid\n-\tcommit, your HEAD becomes \"detached\" and you are no longer on\n+\tcommit, your `HEAD` becomes \"detached\" and you are no longer on\n \tany branch (see below for details).\n +\n-You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+You can use the `@{-N}` syntax to refer to the N-th last\n branch/commit checked out using \"git checkout\" operation. You may\n-also specify `-` which is synonymous to `\"@{-1}\"`.\n+also specify `-` which is synonymous to `@{-1}`.\n +\n-As a special case, you may use `\"A...B\"` as a shortcut for the\n+As a special case, you may use `A...B` as a shortcut for the\n merge base of `A` and `B` if there is exactly one merge base. You can\n leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n <start_point>::\n \tThe name of a commit at which to start the new branch; see\n-\tlinkgit:git-branch[1] for details. Defaults to HEAD.\n+\tlinkgit:git-branch[1] for details. Defaults to `HEAD`.\n \n <tree-ish>::\n \tTree to checkout from (when paths are given). If not specified,\n@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n DETACHED HEAD\n -------------\n-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each\n branch refers to a specific commit. Let's look at a repo with three\n-commits, one of them tagged, and with branch 'master' checked out:\n+commits, one of them tagged, and with branch `master` checked out:\n \n ------------\n            HEAD (refers to branch 'master')\n@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')\n ------------\n \n When a commit is created in this state, the branch is updated to refer to\n-the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n-parent is commit 'c', and then updates branch 'master' to refer to new\n-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n-to commit 'd':\n+the new commit. Specifically, 'git commit' creates a new commit `d`, whose\n+parent is commit `c`, and then updates branch `master` to refer to new\n+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers\n+to commit `d`:\n \n ------------\n $ edit; git add; git commit\n@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n It is sometimes useful to be able to checkout a commit that is not at\n the tip of any named branch, or even to create a new commit that is not\n referenced by a named branch. Let's look at what happens when we\n-checkout commit 'b' (here we show two ways this may be done):\n+checkout commit `b` (here we show two ways this may be done):\n \n ------------\n $ git checkout v2.0  # or\n@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-Notice that regardless of which checkout command we use, HEAD now refers\n-directly to commit 'b'. This is known as being in detached HEAD state.\n-It means simply that HEAD refers to a specific commit, as opposed to\n+Notice that regardless of which checkout command we use, `HEAD` now refers\n+directly to commit `b`. This is known as being in detached `HEAD` state.\n+It means simply that `HEAD` refers to a specific commit, as opposed to\n referring to a named branch. Let's see what happens when we create a commit:\n \n ------------\n@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-There is now a new commit 'e', but it is referenced only by HEAD. We can\n+There is now a new commit `e`, but it is referenced only by `HEAD`. We can\n of course add yet another commit in this state:\n \n ------------\n@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n In fact, we can perform all the normal Git operations. But, let's look\n-at what happens when we then checkout master:\n+at what happens when we then checkout `master`:\n \n ------------\n $ git checkout master\n@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n It is important to realize that at this point nothing refers to commit\n-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted\n by the routine Git garbage collection process, unless we create a reference\n-before that happens. If we have not yet moved away from commit 'f',\n+before that happens. If we have not yet moved away from commit `f`,\n any of these will create a reference to it:\n \n ------------\n@@ -442,19 +442,19 @@ $ git branch foo        <2>\n $ git tag foo           <3>\n ------------\n \n-<1> creates a new branch 'foo', which refers to commit 'f', and then\n-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n-    be in detached HEAD state after this command.\n+<1> creates a new branch `foo`, which refers to commit `f`, and then\n+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer\n+    be in detached `HEAD` state after this command.\n \n-<2> similarly creates a new branch 'foo', which refers to commit 'f',\n-    but leaves HEAD detached.\n+<2> similarly creates a new branch `foo`, which refers to commit `f`,\n+    but leaves `HEAD` detached.\n \n-<3> creates a new tag 'foo', which refers to commit 'f',\n-    leaving HEAD detached.\n+<3> creates a new tag `foo`, which refers to commit `f`,\n+    leaving `HEAD` detached.\n \n-If we have moved away from commit 'f', then we must first recover its object\n+If we have moved away from commit `f`, then we must first recover its object\n name (typically by using git reflog), and then we can create a reference to\n-it. For example, to see the last two commits to which HEAD referred, we\n+it. For example, to see the last two commits to which `HEAD` referred, we\n can use either of these commands:\n \n ------------\n@@ -465,12 +465,12 @@ $ git log -g -2 HEAD\n ARGUMENT DISAMBIGUATION\n -----------------------\n \n-When there is only one argument given and it is not `--` (e.g. \"git\n-checkout abc\"), and when the argument is both a valid `<tree-ish>`\n-(e.g. a branch \"abc\" exists) and a valid `<pathspec>` (e.g. a file\n+When there is only one argument given and it is not `--` (e.g. `git\n+checkout abc`), and when the argument is both a valid `<tree-ish>`\n+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file\n or a directory whose name is \"abc\" exists), Git would usually ask\n you to disambiguate.  Because checking out a branch is so common an\n-operation, however, \"git checkout abc\" takes \"abc\" as a `<tree-ish>`\n+operation, however, `git checkout abc` takes \"abc\" as a `<tree-ish>`\n in such a situation.  Use `git checkout -- <pathspec>` if you want\n to checkout these paths out of the index.\n \n@@ -478,7 +478,7 @@ EXAMPLES\n --------\n \n . The following sequence checks out the `master` branch, reverts\n-  the `Makefile` to two revisions back, deletes hello.c by\n+  the `Makefile` to two revisions back, deletes `hello.c` by\n   mistake, and gets it back from the index.\n +\n ------------\n@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>\n +\n <1> switch branch\n <2> take a file out of another commit\n-<3> restore hello.c from the index\n+<3> restore `hello.c` from the index\n +\n If you want to check out _all_ C source files out of the index,\n you can say\n@@ -519,7 +519,7 @@ $ git checkout -- hello.c\n $ git checkout mytopic\n ------------\n +\n-However, your \"wrong\" branch and correct \"mytopic\" branch may\n+However, your \"wrong\" branch and correct `mytopic` branch may\n differ in files that you have modified locally, in which case\n the above checkout would fail like this:\n +\n-- \n2.21.0\n\n"},{"id":"371028","messageId":"CAN0heSpc7a2Vjh9oM5s5CSJaHNPHBLBFEKuP2iPYXHJYyM+YCw@mail.gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-11-pclouds@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-03-09T12:36:55Z","receivedAt":"2019-03-09T12:37:09Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Fri, 8 Mar 2019 at 11:00, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> ---\n\n(Missing signed-off-by.)\n\n> --- /dev/null\n> +++ b/Documentation/git-switch.txt\n> @@ -0,0 +1,259 @@\n\nIt looks like you base this on git-checkout.txt, which makes sense.\n\n> +--recurse-submodules::\n> +--no-recurse-submodules::\n> +       Using --recurse-submodules will update the content of all initialized\n\nBut it's based on an older copy. You add backticks here in patch 4.\n\nMartin\n"},{"id":"371057","messageId":"CAPig+cRwU_csMj_UYTZUFXgwCz8-R4QSs=08K_oT-1-zEpA2NQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-2-pclouds@gmail.com","subject":"Re: [PATCH v3 01/21] git-checkout.txt: spell out --no-option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-10T03:28:59Z","receivedAt":"2019-03-10T03:32:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 8, 2019 at 4:58 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> It's easier to search for and also less cryptic.\n> ---\n\nYour sign-off is missing from the entire series.\n"},{"id":"371058","messageId":"CAPig+cTR8px1MCq0qXijt1x2+BAgpsKCiRR1hw3hHiVc2NkHAA@mail.gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-19-pclouds@gmail.com","subject":"Re: [PATCH v3 18/21] switch: no worktree status unless real branch switch happens","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-10T03:43:59Z","receivedAt":"2019-03-10T03:44:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 8, 2019 at 4:59 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> When we switch from one branch to another, it makes sense to show a\n> summary of local changes since there could be conflicts, or some files\n> left modified.... When switch is used solely for creating a new\n> branch (and \"switch\" to the same commit) or detaching, we don't really\n> need to show anything.\n>\n> \"git checkout\" does it anyway for historical reasons. But we can start\n> with a clean slate with switch and don't have to.\n>\n> This essentially reverts fa655d8411 (checkout: optimize \"git checkout\n> -b <new_branch>\" - 2018-08-16) and make it default for switch,\n> but also for -B and --detach. Users of big repos are encouraged to\n> move to switch.\n\nI like this last bit. The skip_merge_working_tree() function which\nthis removes was ugly, difficult to maintain, and difficult to get\njust right (and easy to break -- even by changing parts of the system\nwhich one might not expect to impact it).\n"},{"id":"371059","messageId":"CAPig+cTADoua2G2rqyr_2-dqRPsK0Jzsxtkgvv5Gr-iasDA=VA@mail.gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-20-pclouds@gmail.com","subject":"Re: [PATCH v3 19/21] t: add tests for switch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-10T03:59:32Z","receivedAt":"2019-03-10T04:06:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 8, 2019 at 4:59 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\n> @@ -0,0 +1,87 @@\n> +test_expect_success 'switch to a commit' '\n> +       test_must_fail git switch master^{commit}\n> +'\n\nThe title of this test was a bit misleading for me; I would have found\nit easier to understand what's being tested had it said \"switch\nwithout --detach\" or something.\n\n> +test_expect_success 'switch and detach' '\n> +       test_when_finished git switch master &&\n> +       git switch --detach master^{commit} &&\n> +       test_must_fail git symbolic-ref HEAD\n> +'\n\nIn fact, if the two tests were combined, it would have been even\nclearer (for me):\n\n    test_expect_success 'switch and detach' '\n        test_when_finished git switch master &&\n        test_must_fail git switch master^{commit} &&\n        git switch --detach master^{commit} &&\n        test_must_fail git symbolic-ref HEAD\n    '\n\nNot worth a re-roll.\n\n> +test_expect_success 'switch and create branch' '\n> +       test_when_finished git switch master &&\n> +       git switch -c temp master^ &&\n> +       test_cmp_rev master^ refs/heads/temp &&\n> +       echo refs/heads/temp >expected-branch &&\n> +       git symbolic-ref HEAD >actual-branch &&\n> +       test_cmp expected-branch actual-branch\n> +'\n> +\n> +test_expect_success 'force create branch from HEAD' '\n> +       test_when_finished git switch master &&\n> +       git switch --detach master &&\n> +       git switch -C temp &&\n> +       test_cmp_rev master refs/heads/temp &&\n> +       echo refs/heads/temp >expected-branch &&\n> +       git symbolic-ref HEAD >actual-branch &&\n> +       test_cmp expected-branch actual-branch\n> +'\n\nMaybe also demonstrate that -C is actually needed here by leading in\nwith a failing -c:\n\n    ...\n    git switch --detach master &&\n    test_must_fail git switch -c temp &&\n    git switch -C temp &&\n    ...\n\nNot worth a re-roll.\n\n> +test_expect_success 'guess and create branch ' '\n> +       test_when_finished git switch master &&\n> +       test_must_fail git switch foo &&\n> +       git switch --guess foo &&\n> +       echo refs/heads/foo >expected &&\n> +       git symbolic-ref HEAD >actual &&\n> +       test_cmp expected actual\n> +'\n\nThe above suggestions about --detach/-C reflect how you did it in this\ntest, in which you first try \"git switch foo\" without the --guess\noption, expecting it to fail, and then repeat with the option,\nexpecting it to succeed.\n"},{"id":"371072","messageId":"e27faf58-16f4-4f53-1afa-7c5cea868b50@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-20-pclouds@gmail.com","subject":"Re: [PATCH v3 19/21] t: add tests for switch","fromName":"Andrei Rybak","fromEmail":"rybak.a.v@gmail.com","sentAt":"2019-03-10T10:09:34Z","receivedAt":"2019-03-10T10:09:40Z","isPatch":true,"sender":{"key":"rybak.a.v@gmail.com","avatar":"https://avatars.githubusercontent.com/u/624072?v=4"},"body":"On 3/8/19 10:57 AM, Nguyễn Thái Ngọc Duy wrote:\n> ---\n>  t/t2060-switch.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 87 insertions(+)\n>  create mode 100755 t/t2060-switch.sh\n> \n> diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\n> new file mode 100755\n> index 0000000000..1e1e834c1b\n> --- /dev/null\n> +++ b/t/t2060-switch.sh\n> @@ -0,0 +1,87 @@\n> +#!/bin/sh\n> +\n\n[snip]\n\n> +\n> +test_expect_success 'switching ignores file of same branch name' '\n> +\ttest_when_finished git switch master &&\n> +\t: >first-branch &&\n> +\tgit switch first-branch &&\n> +\techo refs/heads/first-branch >expected &&\n> +\tgit symbolic-ref HEAD >actual &&\n> +\ttest_commit expected actual\n\ns/commit/cmp/\n\n> +'\n> +\n> +test_expect_success 'guess and create branch ' '\n> +\ttest_when_finished git switch master &&\n> +\ttest_must_fail git switch foo &&\n> +\tgit switch --guess foo &&\n> +\techo refs/heads/foo >expected &&\n> +\tgit symbolic-ref HEAD >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_done\n> \n\n"},{"id":"371089","messageId":"3A57DE38-A5A7-468B-9509-4244C285896A@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-11-pclouds@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2019-03-10T22:57:45Z","receivedAt":"2019-03-10T23:04:51Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"\n\nOn March 8, 2019 1:57:41 AM PST, \"Nguyễn Thái Ngọc Duy\" <pclouds@gmail.com> wrote:\n>\"git checkout\" doing too many things is a source of confusion for many\n>users (and it even bites old timers sometimes). To remedy that, the\n>command will be split into two new ones: switch and\n>something-to-checkout-paths. The good old \"git checkout\" command is\n>still here and will be until all (or most of users) are sick of it.\n>\n>See the new man page for the final design of switch. The actual\n>implementation though is still pretty much the same as \"git checkout\"\n>and not completely aligned with the man page. Following patches will\n>adjust their behavior to match the man page.\n>---\n> .gitignore                        |   1 +\n> Documentation/config/advice.txt   |  13 +-\n> Documentation/config/branch.txt   |   4 +-\n> Documentation/config/checkout.txt |   9 +-\n> Documentation/config/diff.txt     |   3 +-\n> Documentation/git-checkout.txt    |   4 +\n> Documentation/git-switch.txt      | 259 ++++++\n\n>+<new-branch>::\n>+\tName for the new branch.\n>+\n>+<start-point>::\n>+\tThe name of a commit at which to switch to before creating a\n>+\tnew branch or detach from.\n\nThe wording here (and a few other places) feels awkward to me. I don't really have a better wording but maybe:\n\n---\nThe name of the commit to switch to when creating a new branch or detaching HEAD\n---\n\nThe original has weird tense when using detach.\n\nThere were a few other places like this where the wording was \"or detach from\" but where the verb tense was confusing\n\nThanks,\nJake\n-- \nSent from my Android device with K-9 Mail. Please excuse my brevity.\n"},{"id":"371135","messageId":"CACsJy8DdzbcZzny+v=TESeBxaOsvJcmwY0e54xYvh2m+0zzVXw@mail.gmail.com","threadId":"50360","inReplyTo":"20190309123518.2193053-1-martin.agren@gmail.com","subject":"Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T09:35:31Z","receivedAt":"2019-03-11T09:36:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Mar 9, 2019 at 7:35 PM Martin Ågren <martin.agren@gmail.com> wrote:\n\n> @@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also\nThe part not shown here is\n\n    Using `--recurse-submodules` will update the content of all initialized\n    submodules according to the commit recorded in the superproject. If\n    local modifications in a submodule would be overwritten the checkout\n\nand the --recurse-submodules is rendered incorrectly (not with\nmonospace font, and the quotes remain) because...\n\n>         will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n>         is used, the work trees of submodules will not be updated.\n>         Just like linkgit:git-submodule[1], this will detach the\n> -       submodules HEAD.\n> +       submodules' `HEAD`.\n\n...of this apostrophe, it seems, on both man and html versions. This\nis with asciidoc 8.6.9.\n\nMartin, could you check if your asciidoc (or asciidoctor) behaves the\nsame? If it's not just my buggy asciidoc version, I can turn this to\n\"wil detach `HEAD` of the submodule\" which should fix the problem.\n-- \nDuy\n"},{"id":"371140","messageId":"CAN0heSo94+noufXERexAqodh3GsTFh=f+W+Hd2_0OL3buNfNKg@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8DdzbcZzny+v=TESeBxaOsvJcmwY0e54xYvh2m+0zzVXw@mail.gmail.com","subject":"Re: [PATCH v3 04/21] git-checkout.txt: fix monospace typeset","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-03-11T10:41:43Z","receivedAt":"2019-03-11T10:41:58Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Mon, 11 Mar 2019 at 10:35, Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Sat, Mar 9, 2019 at 7:35 PM Martin Ågren <martin.agren@gmail.com> wrote:\n>\n> > @@ -285,7 +285,7 @@ Note that this option uses the no overlay mode by default (see also\n> The part not shown here is\n>\n>     Using `--recurse-submodules` will update the content of all initialized\n>     submodules according to the commit recorded in the superproject. If\n>     local modifications in a submodule would be overwritten the checkout\n>\n> and the --recurse-submodules is rendered incorrectly (not with\n> monospace font, and the quotes remain) because...\n>\n> >         will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n> >         is used, the work trees of submodules will not be updated.\n> >         Just like linkgit:git-submodule[1], this will detach the\n> > -       submodules HEAD.\n> > +       submodules' `HEAD`.\n>\n> ...of this apostrophe, it seems, on both man and html versions. This\n> is with asciidoc 8.6.9.\n\nOh wow, nicely debugged. Yeah, I see this with 8.6.10, but not with\nAsciidoctor (1.5.5).\n\n> Martin, could you check if your asciidoc (or asciidoctor) behaves the\n> same? If it's not just my buggy asciidoc version, I can turn this to\n> \"wil detach `HEAD` of the submodule\" which should fix the problem.\n\nSounds good.\n\nMartin\n"},{"id":"371142","messageId":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-11-pclouds@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T11:16:02Z","receivedAt":"2019-03-11T11:16:10Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\nHi Duy\n\nOn 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> \"git checkout\" doing too many things is a source of confusion for many\n> users (and it even bites old timers sometimes). To remedy that, the\n> command will be split into two new ones: switch and\n> something-to-checkout-paths. \n\nI think this is a good idea, thanks for working on it. I wonder if it\nwould be a good idea to have the new command refuse to checkout a new\nbranch if there is a cherry-pick/revert/merge/rebase in progress (with\nan option to override the check) as switching branches in the middle of\none of those is likely to be confusing to users (if I do it it is\nnormally because I've forgotten that I've not run 'git whatever\n--continue').\n\n> The good old \"git checkout\" command is\n> still here and will be until all (or most of users) are sick of it.\n> \n> See the new man page for the final design of switch. The actual\n> implementation though is still pretty much the same as \"git checkout\"\n> and not completely aligned with the man page. Following patches will\n> adjust their behavior to match the man page.\n> ---\n>  .gitignore                        |   1 +\n>  Documentation/config/advice.txt   |  13 +-\n>  Documentation/config/branch.txt   |   4 +-\n>  Documentation/config/checkout.txt |   9 +-\n>  Documentation/config/diff.txt     |   3 +-\n>  Documentation/git-checkout.txt    |   4 +\n>  Documentation/git-switch.txt      | 259 ++++++++++++++++++++++++++++++\n>  Documentation/gitattributes.txt   |   3 +-\n>  Documentation/githooks.txt        |   8 +-\n>  Makefile                          |   1 +\n>  builtin.h                         |   1 +\n>  builtin/checkout.c                |  60 +++++--\n>  command-list.txt                  |   1 +\n>  git.c                             |   1 +\n>  14 files changed, 341 insertions(+), 27 deletions(-)\n>  create mode 100644 Documentation/git-switch.txt\n> \n> diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\n> new file mode 100644\n> index 0000000000..ff60ba69fb\n> --- /dev/null\n> +++ b/Documentation/git-switch.txt\n> @@ -0,0 +1,259 @@\n> +git-switch(1)\n> +=============\n> +\n> +NAME\n> +----\n> +git-switch - Switch branches\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git switch' [<options>] [--guess] <branch>\n> +'git switch' [<options>] --detach [<start-point>]\n> +'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n> +\n> +DESCRIPTION\n> +-----------\n> +Switch to a specified branch. The working tree and the index are\n> +updated to match the branch. All new commits will be added to the tip\n> +of this branch.\n> +\n> +Optionally a new branch could be created with either `-c` or `-C`, or\n> +detach the working tree from any branch with `--detach`, along with\n> +switching.\n> +\n> +Switching branches does not require a clean index and working tree\n> +(i.e. no differences compared to 'HEAD'). The operation is aborted\n> +however if the switch leads to loss of local changes, unless told\n> +otherwise.\n> +\n> +OPTIONS\n> +-------\n> +<branch>::\n> +\tBranch to switch to.\n> +\n> +<new-branch>::\n> +\tName for the new branch.\n> +\n> +<start-point>::\n> +\tThe name of a commit at which to switch to before creating a\n> +\tnew branch or detach from.\n> ++\n> +You can use the `\"@{-N}\"` syntax to refer to the N-th last\n> +branch/commit switched to \"git switch\" or \"git checkout\"\n> +operation. You may also specify `-` which is synonymous to `\"@{-1}\"`.\n> ++\n> +As a special case, you may use `\"A...B\"` as a shortcut for the merge\n> +base of `A` and `B` if there is exactly one merge base. You can leave\n> +out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n> +\n> +-c <new-branch>::\n> +--create <new-branch>::\n> +\tCreate a new branch named `<new-branch>` starting at\n> +\t`<start-point>` before switching to the branch. This is a\n> +\tconvenient shortcut for:\n> ++\n> +------------\n> +$ git branch <new-branch>\n> +$ git switch <new-branch>\n> +------------\n> +\n> +-C <new-branch>::\n> +--force-create <new-branch>::\n> +\tSimilar to `--create` except that if `<new-branch>` already\n> +\texists, it will be reset to `<start-point>`. This is a\n> +\tconvenient shortcut for:\n\nIf we're renaming the options to be more meaningful then maybe we should\nconsider a different name for this one - it has nothing to do with\ncreating a branch. Maybe --reset or --update?\n\n> ++\n> +------------\n> +$ git branch -f <new-branch>\n> +$ git switch <new-branch>\n> +------------\n> +\n> +-d::\n> +--detach::\n> +\tSwitch to a commit for inspection and discardable\n> +\texperiments. See the \"DETACHED HEAD\" section in\n> +\tlinkgit:git-checkout[1] for details.\n> +\n> +-g::\n> +--guess::\n> +\tIf `<branch>` is not found but there does exist a tracking\n> +\tbranch in exactly one remote (call it `<remote>`) with a\n> +\tmatching name, treat as equivalent to\n> ++\n> +------------\n> +$ git switch -c <branch> --track <remote>/<branch>\n> +------------\n> ++\n> +If the branch exists in multiple remotes and one of them is named by\n> +the `checkout.defaultRemote` configuration variable, we'll use that\n> +one for the purposes of disambiguation, even if the `<branch>` isn't\n> +unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n> +to always checkout remote branches from there if `<branch>` is\n> +ambiguous but exists on the 'origin' remote. See also\n> +`checkout.defaultRemote` in linkgit:git-config[1].\n> +\n> +-q::\n> +--quiet::\n> +\tQuiet, suppress feedback messages.\n> +\n> +-f::\n> +--force::\n> +\tProceed even if the index or the working tree differs from\n> +\tHEAD. Both the index and working tree are restored to match\n> +\tthe switching target. This is used to throw away local\n> +\tchanges.\n\nI'd always thought that --force meant \"throw away my local changes if\nthey conflict with the new branch\" not \"throw them away regardless\"\n(which is better as it is deterministic). Maybe we can come up with a\nclearer name here --discard-changes? At the moment --force does not\nthrow away conflicts properly (see the script below in my comments about\n--merge).\n\n> +\n> +--progress::\n> +--no-progress::\n> +\tProgress status is reported on the standard error stream\n> +\tby default when it is attached to a terminal, unless `--quiet`\n> +\tis specified. This flag enables progress reporting even if not\n> +\tattached to a terminal, regardless of `--quiet`.\n> +\n> +-t::\n> +--track::\n> +\tWhen creating a new branch, set up \"upstream\" configuration.\n> +\t`-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n> +\tdetails.\n> ++\n> +If no `-c` option is given, the name of the new branch will be derived\n> +from the remote-tracking branch, by looking at the local part of the\n> +refspec configured for the corresponding remote, and then stripping\n> +the initial part up to the \"*\".  This would tell us to use \"hack\" as\n> +the local branch when branching off of \"origin/hack\" (or\n> +\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n> +given name has no slash, or the above guessing results in an empty\n> +name, the guessing is aborted.  You can explicitly give a name with\n> +`-c` in such a case.\n> +\n> +--no-track::\n> +\tDo not set up \"upstream\" configuration, even if the\n> +\tbranch.autoSetupMerge configuration variable is true.\n> +\n> +-m::\n> +--merge::\n> +\tIf you have local modifications to one or more files that are\n> +\tdifferent between the current branch and the branch to which\n> +\tyou are switching, the command refuses to switch branches in\n> +\torder to preserve your modifications in context.  However,\n> +\twith this option, a three-way merge between the current\n> +\tbranch, your working tree contents, and the new branch is\n> +\tdone, and you will be on the new branch.\n\nI was wondering what people felt about making this the default for the\nnew command. If I'm carrying changes over to the new branch then I want\nthem to be merged in, it's annoying to have them carried over most of\nthe time but then sometimes have the checkout fail without specifying\n--merge. At the moment if --merge falls back to doing a three-way merge\nthen it loses any newly staged files that are not in either HEAD. I\nthink they are thrown away in reset_tree(). Here is a script to\ndemonstrate that\n\nd=\"$(mktemp -d)\" &&\ncd \"$d\" &&\ngit init &&\necho a>a &&\ngit add a &&\ngit commit -ama &&\ngit branch topic &&\necho b>a &&\ngit commit -amb &&\ngit checkout topic &&\necho c>a &&\necho x>x &&\ngit add x &&\ngit checkout -m master &&\necho 'x is missing &&\n! git ls-files --error-unmatch x &&\n# a is unmerged\ngit checkout -f topic &&\necho 'a is in stage 1'\ngit ls-files -s a\n\n\nBest Wishes\n\nPhillip\n\n> ++\n> +When a merge conflict happens, the index entries for conflicting\n> +paths are left unmerged, and you need to resolve the conflicts\n> +and mark the resolved paths with `git add` (or `git rm` if the merge\n> +should result in deletion of the path).\n> +\n> +--conflict=<style>::\n> +\tThe same as --merge option above, but changes the way the\n> +\tconflicting hunks are presented, overriding the\n> +\tmerge.conflictStyle configuration variable.  Possible values are\n> +\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n> +\t\"merge\" style, shows the original contents).\n> +\n> +--orphan <new-branch>::\n> +\tCreate a new 'orphan' branch, named `<new-branch>`, started from\n> +\t`<start-point>` and switch to it. See explanation of the same\n> +\toption in linkgit:git-checkout[1] for details.\n> +\n> +--ignore-other-worktrees::\n> +\t`git switch` refuses when the wanted ref is already\n> +\tchecked out by another worktree. This option makes it check\n> +\tthe ref out anyway. In other words, the ref can be held by\n> +\tmore than one worktree.\n> +\n> +--recurse-submodules::\n> +--no-recurse-submodules::\n> +\tUsing --recurse-submodules will update the content of all initialized\n> +\tsubmodules according to the commit recorded in the superproject. If\n> +\tlocal modifications in a submodule would be overwritten the checkout\n> +\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n> +\tis used, the work trees of submodules will not be updated.\n> +\tJust like linkgit:git-submodule[1], this will detach the\n> +\tsubmodules HEAD.\n> +\n> +EXAMPLES\n> +--------\n> +\n> +The following command switches to the \"master\" branch:\n> +\n> +------------\n> +$ git switch master\n> +------------\n> +\n> +After working in the wrong branch, switching to the correct branch\n> +would be done using:\n> +\n> +------------\n> +$ git switch mytopic\n> +------------\n> +\n> +However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n> +in files that you have modified locally, in which case the above\n> +switch would fail like this:\n> +\n> +------------\n> +$ git switch mytopic\n> +error: You have local changes to 'frotz'; not switching branches.\n> +------------\n> +\n> +You can give the `-m` flag to the command, which would try a three-way\n> +merge:\n> +\n> +------------\n> +$ git switch -m mytopic\n> +Auto-merging frotz\n> +------------\n> +\n> +After this three-way merge, the local modifications are _not_\n> +registered in your index file, so `git diff` would show you what\n> +changes you made since the tip of the new branch.\n> +\n> +To switch back to the previous branch before we switched to mytopic\n> +(i.e. \"master\" branch):\n> +\n> +------------\n> +$ git switch -\n> +------------\n> +\n> +You can grow a new branch from any commit. For example, switch to\n> +\"HEAD~3\" and create branch \"fixup\":\n> +\n> +------------\n> +$ git switch -c fixup HEAD~3\n> +Switched to a new branch 'fixup'\n> +------------\n> +\n> +If you want to start a new branch from a remote branch of the same\n> +name, use `-g`:\n> +\n> +------------\n> +$ git switch -g new-topic\n> +Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n> +Switched to a new branch 'new-topic'\n> +------------\n> +\n> +To check out commit \"HEAD~3\" for temporary inspection or experiment\n> +without creating a new branch:\n> +\n> +------------\n> +$ git switch --detach HEAD~3\n> +HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n> +------------\n> +\n> +If it turns out whatever you have done is worth keeping, you can\n> +always create a new name for it (without switching away):\n> +\n> +------------\n> +$ git switch -c good-surprises\n> +------------\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-checkout[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 9b41f81c06..cd0f9fa507 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -112,7 +112,8 @@ Checking-out and checking-in\n>  \n>  These attributes affect how the contents stored in the\n>  repository are copied to the working tree files when commands\n> -such as 'git checkout' and 'git merge' run.  They also affect how\n> +such as 'git switch', 'git checkout'  and 'git merge' run.\n> +They also affect how\n>  Git stores the contents you prepare in the working tree in the\n>  repository upon 'git add' and 'git commit'.\n>  \n> diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\n> index 5bf653c111..8ff72f0613 100644\n> --- a/Documentation/githooks.txt\n> +++ b/Documentation/githooks.txt\n> @@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.\n>  post-checkout\n>  ~~~~~~~~~~~~~\n>  \n> -This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n> +This hook is invoked when a linkgit:git-checkout[1] or\n> +linkgit:git-switch[1] is run after having updated the\n>  worktree.  The hook is given three parameters: the ref of the previous HEAD,\n>  the ref of the new HEAD (which may or may not have changed), and a flag\n>  indicating whether the checkout was a branch checkout (changing branches,\n>  flag=1) or a file checkout (retrieving a file from the index, flag=0).\n> -This hook cannot affect the outcome of `git checkout`.\n> +This hook cannot affect the outcome of `git switch` or `git checkout`.\n>  \n>  It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n>  used. The first parameter given to the hook is the null-ref, the second the\n> @@ -406,7 +407,8 @@ exit with a zero status.\n>  For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n>  in order to emulate `git fetch` that is run in the reverse direction\n>  with `git push`, as the two-tree form of `git read-tree -u -m` is\n> -essentially the same as `git checkout` that switches branches while\n> +essentially the same as `git switch` or `git checkout`\n> +that switches branches while\n>  keeping the local changes in the working tree that do not interfere\n>  with the difference between the branches.\n>  \n> diff --git a/Makefile b/Makefile\n> index 148668368b..8e91db73ad 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X\n>  BUILT_INS += git-show$X\n>  BUILT_INS += git-stage$X\n>  BUILT_INS += git-status$X\n> +BUILT_INS += git-switch$X\n>  BUILT_INS += git-whatchanged$X\n>  \n>  # what 'all' will build and 'install' will install in gitexecdir,\n> diff --git a/builtin.h b/builtin.h\n> index 6538932e99..c64e44450e 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n>  extern int cmd_status(int argc, const char **argv, const char *prefix);\n>  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n>  extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n> +extern int cmd_switch(int argc, const char **argv, const char *prefix);\n>  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tag(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 7d23083282..1eff10dbef 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {\n>  \tNULL,\n>  };\n>  \n> +static const char * const switch_branch_usage[] = {\n> +\tN_(\"git switch [<options>] [<branch>]\"),\n> +\tNULL,\n> +};\n> +\n>  struct checkout_opts {\n>  \tint patch_mode;\n>  \tint quiet;\n> @@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n>  \treturn newopts;\n>  }\n>  \n> -int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +static int checkout_main(int argc, const char **argv, const char *prefix,\n> +\t\t\t struct checkout_opts *opts, struct option *options,\n> +\t\t\t const char * const usagestr[])\n>  {\n> -\tstruct checkout_opts real_opts;\n> -\tstruct checkout_opts *opts = &real_opts;\n>  \tstruct branch_info new_branch_info;\n>  \tint dwim_remotes_matched = 0;\n>  \tint dwim_new_local_branch;\n> -\tstruct option *options = NULL;\n>  \n> -\tmemset(opts, 0, sizeof(*opts));\n>  \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n>  \topts->overwrite_ignore = 1;\n>  \topts->prefix = prefix;\n>  \topts->show_progress = -1;\n>  \topts->overlay_mode = -1;\n> -\topts->no_dwim_new_local_branch = 0;\n>  \n>  \tgit_config(git_checkout_config, opts);\n>  \n>  \topts->track = BRANCH_TRACK_UNSPECIFIED;\n>  \n> -\toptions = parse_options_dup(options);\n> -\toptions = add_common_options(opts, options);\n> -\toptions = add_switch_branch_options(opts, options);\n> -\toptions = add_checkout_path_options(opts, options);\n> -\n> -\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n> +\targc = parse_options(argc, argv, prefix, options, usagestr,\n>  \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n>  \n>  \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n> @@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\treturn checkout_branch(opts, &new_branch_info);\n>  \t}\n>  }\n> +\n> +int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.no_dwim_new_local_branch = 0;\n> +\n> +\toptions = parse_options_dup(options);\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\toptions = add_checkout_path_options(&opts, options);\n> +\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, checkout_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> +\n> +int cmd_switch(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.no_dwim_new_local_branch = 0;\n> +\n> +\toptions = parse_options_dup(options);\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, switch_branch_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> diff --git a/command-list.txt b/command-list.txt\n> index 3a9af104b5..13317f47d4 100644\n> --- a/command-list.txt\n> +++ b/command-list.txt\n> @@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n>  git-stripspace                          purehelpers\n>  git-submodule                           mainporcelain\n>  git-svn                                 foreignscminterface\n> +git-switch                              mainporcelain           history\n>  git-symbolic-ref                        plumbingmanipulators\n>  git-tag                                 mainporcelain           history\n>  git-unpack-file                         plumbinginterrogators\n> diff --git a/git.c b/git.c\n> index 2014aab6b8..39582cf511 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"stripspace\", cmd_stripspace },\n>  \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n> +\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n>  \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n>  \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n> \n\n"},{"id":"371143","messageId":"CACsJy8DrrEg++wUszU2B1zJ_gvO1WC8MXXa53ZpMnObgqy=AeQ@mail.gmail.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T11:47:22Z","receivedAt":"2019-03-11T11:47:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n>\n> Hi Duy\n>\n> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > \"git checkout\" doing too many things is a source of confusion for many\n> > users (and it even bites old timers sometimes). To remedy that, the\n> > command will be split into two new ones: switch and\n> > something-to-checkout-paths.\n>\n> I think this is a good idea, thanks for working on it. I wonder if it\n> would be a good idea to have the new command refuse to checkout a new\n> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> an option to override the check) as switching branches in the middle of\n> one of those is likely to be confusing to users (if I do it it is\n> normally because I've forgotten that I've not run 'git whatever\n> --continue').\n\nInteresting. I think this would be a good default if we have an escape\nhatch (which could even come later). I often wander off to some other\nbranch and go back. But then half the time I end up forgetting I'm in\na middle of something and just \"git rebase --quit\" :P\n\nOf course with git-stash (*) and git-worktree, I guess there's little\nreason to just switch away from a something-in-progress worktree. I'll\ntry to implement this in the next round, unless someone objects.\n\n(*) I hope git-stash remembers and restores \"something-in-progress\"\nstatus. Dunno. Never used it much.\n\n> > +-C <new-branch>::\n> > +--force-create <new-branch>::\n> > +     Similar to `--create` except that if `<new-branch>` already\n> > +     exists, it will be reset to `<start-point>`. This is a\n> > +     convenient shortcut for:\n>\n> If we're renaming the options to be more meaningful then maybe we should\n> consider a different name for this one - it has nothing to do with\n> creating a branch. Maybe --reset or --update?\n\n-C can also create a new branch like -c though. --reset or --update\ndoes not convey that (and --update sounds a bit too safe). Another\noption is --recreate.\n\n> > +-f::\n> > +--force::\n> > +     Proceed even if the index or the working tree differs from\n> > +     HEAD. Both the index and working tree are restored to match\n> > +     the switching target. This is used to throw away local\n> > +     changes.\n>\n> I'd always thought that --force meant \"throw away my local changes if\n> they conflict with the new branch\" not \"throw them away regardless\"\n> (which is better as it is deterministic). Maybe we can come up with a\n> clearer name here --discard-changes? At the moment --force does not\n> throw away conflicts properly (see the script below in my comments about\n> --merge).\n\nYeah if you want to redefine --force, now is a very good time.\nPersonally I'd rather have separate options than the \"one catch all\"\n--force (or worse, multiple of --force). I'll leave this for the\ncommunity to decide.\n\nAdding Elijah too. He also had some concern about \"git restore\n--force\". Maybe he's interested in this as well.\n-- \nDuy\n"},{"id":"371167","messageId":"f63a7ae7-0a08-02f2-23e6-4389d8fda729@gmail.com","threadId":"50360","inReplyTo":"CACsJy8DrrEg++wUszU2B1zJ_gvO1WC8MXXa53ZpMnObgqy=AeQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T17:03:25Z","receivedAt":"2019-03-11T17:03:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Duy\n\nOn 11/03/2019 11:47, Duy Nguyen wrote:\n> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>>\n>> Hi Duy\n>>\n>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n>>> \"git checkout\" doing too many things is a source of confusion for many\n>>> users (and it even bites old timers sometimes). To remedy that, the\n>>> command will be split into two new ones: switch and\n>>> something-to-checkout-paths.\n>>\n>> I think this is a good idea, thanks for working on it. I wonder if it\n>> would be a good idea to have the new command refuse to checkout a new\n>> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n>> an option to override the check) as switching branches in the middle of\n>> one of those is likely to be confusing to users (if I do it it is\n>> normally because I've forgotten that I've not run 'git whatever\n>> --continue').\n> \n> Interesting. I think this would be a good default if we have an escape\n> hatch (which could even come later). I often wander off to some other\n> branch and go back. But then half the time I end up forgetting I'm in\n> a middle of something and just \"git rebase --quit\" :P\n> \n> Of course with git-stash (*) and git-worktree, I guess there's little\n> reason to just switch away from a something-in-progress worktree. I'll\n> try to implement this in the next round, unless someone objects.\n> \n> (*) I hope git-stash remembers and restores \"something-in-progress\"\n> status. Dunno. Never used it much.\n\nI don't think it does. For rebase it's non trivial as it needs to \nremember the refs under refs/rewritten and stop gc from collecting any \nof them or the original head (in theory the todo list can contain \ncommits that the user has added from other branches as well so they'd \nalso need to be protected from gc). For cherry-pick there are gc issues \nas well.\n\nBest Wishes\n\nPhillip\n\n> \n>>> +-C <new-branch>::\n>>> +--force-create <new-branch>::\n>>> +     Similar to `--create` except that if `<new-branch>` already\n>>> +     exists, it will be reset to `<start-point>`. This is a\n>>> +     convenient shortcut for:\n>>\n>> If we're renaming the options to be more meaningful then maybe we should\n>> consider a different name for this one - it has nothing to do with\n>> creating a branch. Maybe --reset or --update?\n> \n> -C can also create a new branch like -c though. --reset or --update\n> does not convey that (and --update sounds a bit too safe). Another\n> option is --recreate.\n> \n>>> +-f::\n>>> +--force::\n>>> +     Proceed even if the index or the working tree differs from\n>>> +     HEAD. Both the index and working tree are restored to match\n>>> +     the switching target. This is used to throw away local\n>>> +     changes.\n>>\n>> I'd always thought that --force meant \"throw away my local changes if\n>> they conflict with the new branch\" not \"throw them away regardless\"\n>> (which is better as it is deterministic). Maybe we can come up with a\n>> clearer name here --discard-changes? At the moment --force does not\n>> throw away conflicts properly (see the script below in my comments about\n>> --merge).\n> \n> Yeah if you want to redefine --force, now is a very good time.\n> Personally I'd rather have separate options than the \"one catch all\"\n> --force (or worse, multiple of --force). I'll leave this for the\n> community to decide.\n> \n> Adding Elijah too. He also had some concern about \"git restore\n> --force\". Maybe he's interested in this as well.\n> \n"},{"id":"371168","messageId":"CABPp-BFStPOtA_OGrS3HCe_XqWN1roBnzss3nefcHdQg=9eYLQ@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8DrrEg++wUszU2B1zJ_gvO1WC8MXXa53ZpMnObgqy=AeQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T17:24:52Z","receivedAt":"2019-03-11T17:25:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >\n> > Hi Duy\n> >\n> > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > > \"git checkout\" doing too many things is a source of confusion for many\n> > > users (and it even bites old timers sometimes). To remedy that, the\n> > > command will be split into two new ones: switch and\n> > > something-to-checkout-paths.\n> >\n> > I think this is a good idea, thanks for working on it. I wonder if it\n> > would be a good idea to have the new command refuse to checkout a new\n> > branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> > an option to override the check) as switching branches in the middle of\n> > one of those is likely to be confusing to users (if I do it it is\n> > normally because I've forgotten that I've not run 'git whatever\n> > --continue').\n>\n> Interesting. I think this would be a good default if we have an escape\n> hatch (which could even come later). I often wander off to some other\n> branch and go back. But then half the time I end up forgetting I'm in\n> a middle of something and just \"git rebase --quit\" :P\n>\n> Of course with git-stash (*) and git-worktree, I guess there's little\n> reason to just switch away from a something-in-progress worktree. I'll\n> try to implement this in the next round, unless someone objects.\n\nNo objection here; I like this idea.\n\n> (*) I hope git-stash remembers and restores \"something-in-progress\"\n> status. Dunno. Never used it much.\n>\n> > > +-C <new-branch>::\n> > > +--force-create <new-branch>::\n> > > +     Similar to `--create` except that if `<new-branch>` already\n> > > +     exists, it will be reset to `<start-point>`. This is a\n> > > +     convenient shortcut for:\n> >\n> > If we're renaming the options to be more meaningful then maybe we should\n> > consider a different name for this one - it has nothing to do with\n> > creating a branch. Maybe --reset or --update?\n>\n> -C can also create a new branch like -c though. --reset or --update\n> does not convey that (and --update sounds a bit too safe). Another\n> option is --recreate.\n\nMaybe --recreate, but I don't see as much of a problem with the\noriginal name you gave the option.  Which name is better probably\ndepends on how you envision its usage.  If you view this option as\nonly being used if/when '-c' fails (perhaps Phillip sees it that\nway?), then it'd make sense to use --recreate instead.  But if you\nthink some might adopt a workflow where they just use -C without first\ntrying -c (\"create this branch, and I don't care if I made it before\njust create it here\"), then --force-create makes sense.\n\nAnother option, possibly showing my lack of understanding why this\nflag was useful in the first place: just drop this set of flags from\nthis command.  People can switch to the branch and then use reset\n--hard <startpoint>, right?  Or (if they don't care about the reflog,\nwhich they probably don't) delete the branch first and then recreate\nit?  Not sure why we need to give another way to do these operations.\n(In contrast, I see -c as being used frequently enough to have merit\neven if it could be implemented as two separate commands.)\n\nI don't have much of an opinion about which of these three is the best\noption here (I'm slightly biased towards just jettisoning the option,\nbut I understand there might be a good reason for it even if I don't\nknow what it is), so really I'm just giving some food for thought on\nthis one.\n\n> > > +-f::\n> > > +--force::\n> > > +     Proceed even if the index or the working tree differs from\n> > > +     HEAD. Both the index and working tree are restored to match\n> > > +     the switching target. This is used to throw away local\n> > > +     changes.\n> >\n> > I'd always thought that --force meant \"throw away my local changes if\n> > they conflict with the new branch\" not \"throw them away regardless\"\n> > (which is better as it is deterministic). Maybe we can come up with a\n> > clearer name here --discard-changes? At the moment --force does not\n> > throw away conflicts properly (see the script below in my comments about\n> > --merge).\n>\n> Yeah if you want to redefine --force, now is a very good time.\n> Personally I'd rather have separate options than the \"one catch all\"\n> --force (or worse, multiple of --force). I'll leave this for the\n> community to decide.\n>\n> Adding Elijah too. He also had some concern about \"git restore\n> --force\". Maybe he's interested in this as well.\n\nI like Phillip's suggestion of --discard-changes.  I also like how he\ncame up with a simple testcase showing one bug each with checkout's\ncurrent -m and -f handling; we should fix those.\n"},{"id":"371171","messageId":"CABPp-BEBudobnduipQrSvyQWQ7Hb4WmmoptCQ+FyY8BRPp7_ZA@mail.gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-11-pclouds@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T17:54:16Z","receivedAt":"2019-03-11T17:54:31Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"A few other comments that I thought of while responding elsewhere in\nthe thread that didn't make sense to include elsewhere...\n\nOn Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> \"git checkout\" doing too many things is a source of confusion for many\n> users (and it even bites old timers sometimes). To remedy that, the\n> command will be split into two new ones: switch and\n> something-to-checkout-paths. The good old \"git checkout\" command is\n\ns/something-to-checkout-paths/restore/ ?\n\n> +-g::\n> +--guess::\n> +       If `<branch>` is not found but there does exist a tracking\n> +       branch in exactly one remote (call it `<remote>`) with a\n> +       matching name, treat as equivalent to\n> ++\n> +------------\n> +$ git switch -c <branch> --track <remote>/<branch>\n> +------------\n\nI'm not sure if it'd be better or worse to simplify this to\n   git switch --track <remote>/<branch>\nThoughts?\n\n> ++\n> +If the branch exists in multiple remotes and one of them is named by\n> +the `checkout.defaultRemote` configuration variable, we'll use that\n> +one for the purposes of disambiguation, even if the `<branch>` isn't\n> +unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n> +to always checkout remote branches from there if `<branch>` is\n> +ambiguous but exists on the 'origin' remote. See also\n> +`checkout.defaultRemote` in linkgit:git-config[1].\n> +\n> +-q::\n> +--quiet::\n> +       Quiet, suppress feedback messages.\n\n--quiet and --progress should be adjacent in the man-page since they\nare touching upon the same concept.  It'd be nice if we had a way to\ngroup similar items like this besides simply putting them together,\nbut we can at least do that.\n\n> +\n> +-f::\n> +--force::\n> +       Proceed even if the index or the working tree differs from\n> +       HEAD. Both the index and working tree are restored to match\n> +       the switching target. This is used to throw away local\n> +       changes.\n\n--force (or --discard-changes as mentioned elsewhere in this thread),\n-m/--merge, and --conflict are also similar options and should be\nadjacent in the man-page; they all reflect on how to handle switching\nwhen local changes affect the same paths that different between HEAD\nand the other branch.\n\n> +--progress::\n> +--no-progress::\n> +       Progress status is reported on the standard error stream\n> +       by default when it is attached to a terminal, unless `--quiet`\n> +       is specified. This flag enables progress reporting even if not\n> +       attached to a terminal, regardless of `--quiet`.\n\nThis again makes me curious what --quiet actually supresses; in the\ncase of branch switching, are there any non-warning informational\nmessages other than progress reports that are printed?  If not, the\nextra sentence at the end of the description for --progress can (and\nprobably should) be stripped.\n\n> +\n> +-t::\n> +--track::\n> +       When creating a new branch, set up \"upstream\" configuration.\n> +       `-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n> +       details.\n> ++\n> +If no `-c` option is given, the name of the new branch will be derived\n> +from the remote-tracking branch, by looking at the local part of the\n> +refspec configured for the corresponding remote, and then stripping\n> +the initial part up to the \"*\".  This would tell us to use \"hack\" as\n> +the local branch when branching off of \"origin/hack\" (or\n> +\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n> +given name has no slash, or the above guessing results in an empty\n> +name, the guessing is aborted.  You can explicitly give a name with\n> +`-c` in such a case.\n\nSlightly disappointed that we couldn't remove --track; it has always\nseemed a bit complicated.  But I don't see how to do so, since people\ndo want upstream branches to be tracked with their new branches.\n\n> +\n> +--no-track::\n> +       Do not set up \"upstream\" configuration, even if the\n> +       branch.autoSetupMerge configuration variable is true.\n> +\n> +-m::\n> +--merge::\n> +       If you have local modifications to one or more files that are\n> +       different between the current branch and the branch to which\n> +       you are switching, the command refuses to switch branches in\n> +       order to preserve your modifications in context.  However,\n> +       with this option, a three-way merge between the current\n> +       branch, your working tree contents, and the new branch is\n> +       done, and you will be on the new branch.\n> ++\n> +When a merge conflict happens, the index entries for conflicting\n> +paths are left unmerged, and you need to resolve the conflicts\n> +and mark the resolved paths with `git add` (or `git rm` if the merge\n> +should result in deletion of the path).\n\nNow that Phillip highlighted issues with -m and -f, it's hard not to\nwonder about other corner cases.  For example, what if the user made\nsome changes, staged them, then made more changes, then tried to 'git\ncheckout -m <other branch>'?  That's no longer a three-way merge, but\nfour way.  How does that work?  Does it just rely on merge-recursive's\n(poorly defined) choice of when to bail out and when to permit such\ncraziness?\n\n> +--conflict=<style>::\n> +       The same as --merge option above, but changes the way the\n> +       conflicting hunks are presented, overriding the\n> +       merge.conflictStyle configuration variable.  Possible values are\n> +       \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> +       \"merge\" style, shows the original contents).\n> +\n> +--orphan <new-branch>::\n> +       Create a new 'orphan' branch, named `<new-branch>`, started from\n> +       `<start-point>` and switch to it. See explanation of the same\n> +       option in linkgit:git-checkout[1] for details.\n\nSigh...does this mean --orphan will remain broken?  It has always\ndriven me crazy that it leaves you with a fully populated rather than\nan empty index.  It seemed broken to me before I figured out the\nspecial usecase, though it still seemed like the wrong default (an\nempty index wouldn't surprise due to the \"orphan\" name, but a full one\ndoes to those without the special usecase in mind).  Oh well, that's a\nmuch smaller battle than the big picture of getting switch and restore\nin place, and I don't want to derail the bigger picture; anything\nusing --orphan is a somewhat special case anyway.\n\n> +You can give the `-m` flag to the command, which would try a three-way\n> +merge:\n> +\n> +------------\n> +$ git switch -m mytopic\n> +Auto-merging frotz\n> +------------\n> +\n> +After this three-way merge, the local modifications are _not_\n> +registered in your index file, so `git diff` would show you what\n> +changes you made since the tip of the new branch.\n\n...even if the local modifications were registered in the index file\nbefore?  Is this why Phillip's \"x\" was missing and how it avoided\ndoing a four-way merge?  I guess it kinda makes sense, view from this\nangle, but I'm not so sure I like it.  Hmm....\n"},{"id":"371179","messageId":"CABPp-BH+RRaC1bw2_4e7C8PicnF=qT+c9TcRv+E1efbx6X=Akw@mail.gmail.com","threadId":"50360","inReplyTo":"3A57DE38-A5A7-468B-9509-4244C285896A@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T19:00:28Z","receivedAt":"2019-03-11T19:00:43Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Mar 10, 2019 at 5:29 PM Jacob Keller <jacob.keller@gmail.com> wrote:\n>\n> On March 8, 2019 1:57:41 AM PST, \"Nguyễn Thái Ngọc Duy\" <pclouds@gmail.com> wrote:\n\n> >+<new-branch>::\n> >+      Name for the new branch.\n> >+\n> >+<start-point>::\n> >+      The name of a commit at which to switch to before creating a\n> >+      new branch or detach from.\n>\n> The wording here (and a few other places) feels awkward to me. I don't really have a better wording but maybe:\n>\n> ---\n> The name of the commit to switch to when creating a new branch or detaching HEAD\n> ---\n\nI also struggle a bit to get better wording in this case.  But maybe\nthrowing another idea will inspire someone to synthesize (or rewrite)\nthese into something better, so...\n\n--\nThe starting point for the new branch.  Specifying a <start-point>\nallows you to create a branch based on some other point in history\nthan where HEAD currently points.  (Or, in the case of --detach,\nallows you to inspect and detach from some other point.)\n--\n\nI know it's slightly on the longer side, but it's the best I could think of.\n"},{"id":"371192","messageId":"d78392c3-7282-43ab-b88c-aa13fb5f937a@gmail.com","threadId":"50360","inReplyTo":"CABPp-BFStPOtA_OGrS3HCe_XqWN1roBnzss3nefcHdQg=9eYLQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T20:51:31Z","receivedAt":"2019-03-11T20:51:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 11/03/2019 17:24, Elijah Newren wrote:\n> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>>\n>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>>\n>>> Hi Duy\n>>>\n>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n>>>> \"git checkout\" doing too many things is a source of confusion for many\n>>>> users (and it even bites old timers sometimes). To remedy that, the\n>>>> command will be split into two new ones: switch and\n>>>> something-to-checkout-paths.\n>>>\n>>> I think this is a good idea, thanks for working on it. I wonder if it\n>>> would be a good idea to have the new command refuse to checkout a new\n>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n>>> an option to override the check) as switching branches in the middle of\n>>> one of those is likely to be confusing to users (if I do it it is\n>>> normally because I've forgotten that I've not run 'git whatever\n>>> --continue').\n>>\n>> Interesting. I think this would be a good default if we have an escape\n>> hatch (which could even come later). I often wander off to some other\n>> branch and go back. But then half the time I end up forgetting I'm in\n>> a middle of something and just \"git rebase --quit\" :P\n>>\n>> Of course with git-stash (*) and git-worktree, I guess there's little\n>> reason to just switch away from a something-in-progress worktree. I'll\n>> try to implement this in the next round, unless someone objects.\n> \n> No objection here; I like this idea.\n> \n>> (*) I hope git-stash remembers and restores \"something-in-progress\"\n>> status. Dunno. Never used it much.\n>>\n>>>> +-C <new-branch>::\n>>>> +--force-create <new-branch>::\n>>>> +     Similar to `--create` except that if `<new-branch>` already\n>>>> +     exists, it will be reset to `<start-point>`. This is a\n>>>> +     convenient shortcut for:\n>>>\n>>> If we're renaming the options to be more meaningful then maybe we should\n>>> consider a different name for this one - it has nothing to do with\n>>> creating a branch. Maybe --reset or --update?\n>>\n>> -C can also create a new branch like -c though. --reset or --update\n>> does not convey that (and --update sounds a bit too safe). Another\n>> option is --recreate.\n> \n> Maybe --recreate, but I don't see as much of a problem with the\n> original name you gave the option.  Which name is better probably\n> depends on how you envision its usage.  If you view this option as\n> only being used if/when '-c' fails (perhaps Phillip sees it that\n> way?), \n\nYes I was thinking it was used to reset an existing branch in order to \nreuse the name, --recreate is a good suggestion in that context.\n\n> then it'd make sense to use --recreate instead.  But if you\n> think some might adopt a workflow where they just use -C without first\n> trying -c (\"create this branch, and I don't care if I made it before\n> just create it here\"), then --force-create makes sense.\n> \n> Another option, possibly showing my lack of understanding why this\n> flag was useful in the first place: just drop this set of flags from\n> this command.  People can switch to the branch and then use reset\n> --hard <startpoint>, right?  Or (if they don't care about the reflog,\n> which they probably don't) delete the branch first and then recreate\n> it?  Not sure why we need to give another way to do these operations.\n> (In contrast, I see -c as being used frequently enough to have merit\n> even if it could be implemented as two separate commands.)\n\nI tend to agree with this but that's probably because I don't really use \ncheckout -B. I'm not sure if it's widely used or not. I do find checkout \n-b convenient though.\n\n> I don't have much of an opinion about which of these three is the best\n> option here (I'm slightly biased towards just jettisoning the option,\n> but I understand there might be a good reason for it even if I don't\n> know what it is), so really I'm just giving some food for thought on\n> this one.\n> \n>>>> +-f::\n>>>> +--force::\n>>>> +     Proceed even if the index or the working tree differs from\n>>>> +     HEAD. Both the index and working tree are restored to match\n>>>> +     the switching target. This is used to throw away local\n>>>> +     changes.\n>>>\n>>> I'd always thought that --force meant \"throw away my local changes if\n>>> they conflict with the new branch\" not \"throw them away regardless\"\n>>> (which is better as it is deterministic). Maybe we can come up with a\n>>> clearer name here --discard-changes? At the moment --force does not\n>>> throw away conflicts properly (see the script below in my comments about\n>>> --merge).\n>>\n>> Yeah if you want to redefine --force, now is a very good time.\n>> Personally I'd rather have separate options than the \"one catch all\"\n>> --force (or worse, multiple of --force). I'll leave this for the\n>> community to decide.\n>>\n>> Adding Elijah too. He also had some concern about \"git restore\n>> --force\". Maybe he's interested in this as well.\n> \n> I like Phillip's suggestion of --discard-changes.  I also like how he\n> came up with a simple testcase showing one bug each with checkout's\n> current -m and -f handling; we should fix those.\n\nWith regard to discarding conflicts, do we want it to clear up any state \nassociated with the conflicts (like reset)? They rarely happen in \nisolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure \nwhat it should do in the middle of a rebase or when cherry-picking a \nrange of commits. I think it would be surprising if it was the \nequivalent of rebase/cherry-pick --quit but just clearing the conflicts \nin those contexts may not be very useful in practice.\n\nBest Wishes\n\nPhillip\n"},{"id":"371197","messageId":"CABPp-BH8FjK9LrZoctpxsuKW13FW_2qfk9wx=VXCZ5U+3B8b+Q@mail.gmail.com","threadId":"50360","inReplyTo":"d78392c3-7282-43ab-b88c-aa13fb5f937a@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-11T22:04:59Z","receivedAt":"2019-03-11T22:05:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 11, 2019 at 1:51 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 11/03/2019 17:24, Elijah Newren wrote:\n> > On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> >>>> \"git checkout\" doing too many things is a source of confusion for many\n> >>>> users (and it even bites old timers sometimes). To remedy that, the\n> >>>> command will be split into two new ones: switch and\n> >>>> something-to-checkout-paths.\n> >>>\n> >>> I think this is a good idea, thanks for working on it. I wonder if it\n> >>> would be a good idea to have the new command refuse to checkout a new\n> >>> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> >>> an option to override the check) as switching branches in the middle of\n> >>> one of those is likely to be confusing to users (if I do it it is\n> >>> normally because I've forgotten that I've not run 'git whatever\n> >>> --continue').\n> >>\n> >> Interesting. I think this would be a good default if we have an escape\n> >> hatch (which could even come later). I often wander off to some other\n> >> branch and go back. But then half the time I end up forgetting I'm in\n> >> a middle of something and just \"git rebase --quit\" :P\n> >>\n> >> Of course with git-stash (*) and git-worktree, I guess there's little\n> >> reason to just switch away from a something-in-progress worktree. I'll\n> >> try to implement this in the next round, unless someone objects.\n> >\n> > No objection here; I like this idea.\n> >\n\nKeeping this hunk since it's now relevant to the comment below...\n\n> >>>> +-f::\n> >>>> +--force::\n> >>>> +     Proceed even if the index or the working tree differs from\n> >>>> +     HEAD. Both the index and working tree are restored to match\n> >>>> +     the switching target. This is used to throw away local\n> >>>> +     changes.\n> >>>\n> >>> I'd always thought that --force meant \"throw away my local changes if\n> >>> they conflict with the new branch\" not \"throw them away regardless\"\n> >>> (which is better as it is deterministic). Maybe we can come up with a\n> >>> clearer name here --discard-changes? At the moment --force does not\n> >>> throw away conflicts properly (see the script below in my comments about\n> >>> --merge).\n> >>\n> >> Yeah if you want to redefine --force, now is a very good time.\n> >> Personally I'd rather have separate options than the \"one catch all\"\n> >> --force (or worse, multiple of --force). I'll leave this for the\n> >> community to decide.\n> >>\n> >> Adding Elijah too. He also had some concern about \"git restore\n> >> --force\". Maybe he's interested in this as well.\n> >\n> > I like Phillip's suggestion of --discard-changes.  I also like how he\n> > came up with a simple testcase showing one bug each with checkout's\n> > current -m and -f handling; we should fix those.\n>\n> With regard to discarding conflicts, do we want it to clear up any state\n> associated with the conflicts (like reset)? They rarely happen in\n> isolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure\n> what it should do in the middle of a rebase or when cherry-picking a\n> range of commits. I think it would be surprising if it was the\n> equivalent of rebase/cherry-pick --quit but just clearing the conflicts\n> in those contexts may not be very useful in practice.\n\nYou already suggested above (outside the context of --discard-changes)\nthat we should just error out if there is some special mid-operation\nstate (be it from a merge, cherry-pick, rebase, or bisect).  The user\ncan then manually resolve the operation first, or, perhaps use a\nspecial override to force the switch command to proceed despite the\npresence of mid-operation state.\n\nPersonally, I'm leaning towards --discard-changes operating within\nthat same context; I think that mid-operation special state should\nrequire a more explicit and operation-specific step to remove (e.g.\nrebase --quit).\n"},{"id":"371236","messageId":"47d1d587-9c75-cc97-3006-6cc22707a70a@gmail.com","threadId":"50360","inReplyTo":"CABPp-BH8FjK9LrZoctpxsuKW13FW_2qfk9wx=VXCZ5U+3B8b+Q@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-12T10:58:38Z","receivedAt":"2019-03-12T10:58:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nOn 11/03/2019 22:04, Elijah Newren wrote:\n> On Mon, Mar 11, 2019 at 1:51 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 11/03/2019 17:24, Elijah Newren wrote:\n>>> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>>>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n>>>>>> \"git checkout\" doing too many things is a source of confusion for many\n>>>>>> users (and it even bites old timers sometimes). To remedy that, the\n>>>>>> command will be split into two new ones: switch and\n>>>>>> something-to-checkout-paths.\n>>>>>\n>>>>> I think this is a good idea, thanks for working on it. I wonder if it\n>>>>> would be a good idea to have the new command refuse to checkout a new\n>>>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n>>>>> an option to override the check) as switching branches in the middle of\n>>>>> one of those is likely to be confusing to users (if I do it it is\n>>>>> normally because I've forgotten that I've not run 'git whatever\n>>>>> --continue').\n>>>>\n>>>> Interesting. I think this would be a good default if we have an escape\n>>>> hatch (which could even come later). I often wander off to some other\n>>>> branch and go back. But then half the time I end up forgetting I'm in\n>>>> a middle of something and just \"git rebase --quit\" :P\n>>>>\n>>>> Of course with git-stash (*) and git-worktree, I guess there's little\n>>>> reason to just switch away from a something-in-progress worktree. I'll\n>>>> try to implement this in the next round, unless someone objects.\n>>>\n>>> No objection here; I like this idea.\n>>>\n> \n> Keeping this hunk since it's now relevant to the comment below...\n> \n>>>>>> +-f::\n>>>>>> +--force::\n>>>>>> +     Proceed even if the index or the working tree differs from\n>>>>>> +     HEAD. Both the index and working tree are restored to match\n>>>>>> +     the switching target. This is used to throw away local\n>>>>>> +     changes.\n>>>>>\n>>>>> I'd always thought that --force meant \"throw away my local changes if\n>>>>> they conflict with the new branch\" not \"throw them away regardless\"\n>>>>> (which is better as it is deterministic). Maybe we can come up with a\n>>>>> clearer name here --discard-changes? At the moment --force does not\n>>>>> throw away conflicts properly (see the script below in my comments about\n>>>>> --merge).\n>>>>\n>>>> Yeah if you want to redefine --force, now is a very good time.\n>>>> Personally I'd rather have separate options than the \"one catch all\"\n>>>> --force (or worse, multiple of --force). I'll leave this for the\n>>>> community to decide.\n>>>>\n>>>> Adding Elijah too. He also had some concern about \"git restore\n>>>> --force\". Maybe he's interested in this as well.\n>>>\n>>> I like Phillip's suggestion of --discard-changes.  I also like how he\n>>> came up with a simple testcase showing one bug each with checkout's\n>>> current -m and -f handling; we should fix those.\n>>\n>> With regard to discarding conflicts, do we want it to clear up any state\n>> associated with the conflicts (like reset)? They rarely happen in\n>> isolation, there's a MERGE_HEAD or CHERRY_PICK_HEAD etc. I'm not sure\n>> what it should do in the middle of a rebase or when cherry-picking a\n>> range of commits. I think it would be surprising if it was the\n>> equivalent of rebase/cherry-pick --quit but just clearing the conflicts\n>> in those contexts may not be very useful in practice.\n> \n> You already suggested above (outside the context of --discard-changes)\n> that we should just error out if there is some special mid-operation\n> state (be it from a merge, cherry-pick, rebase, or bisect).  The user\n> can then manually resolve the operation first, or, perhaps use a\n> special override to force the switch command to proceed despite the\n> presence of mid-operation state.\n> \n> Personally, I'm leaning towards --discard-changes operating within\n> that same context; I think that mid-operation special state should\n> require a more explicit and operation-specific step to remove (e.g.\n> rebase --quit).\n\nI think that makes sense, I was wondering if --discard-changes should \nremove the state of a conflicted merge/single cherry-pick/revert but it \nis probably simpler and easier to understand just to error out. I think \nit should still clean up conflicts from other checkouts and applying \nstashes though as there is no other state in those cases.\n\nBest Wishes\n\nPhillip\n"},{"id":"371238","messageId":"ca6f8e4b-d60c-877c-52e7-fb91c99cd3a5@gmail.com","threadId":"50360","inReplyTo":"CABPp-BEBudobnduipQrSvyQWQ7Hb4WmmoptCQ+FyY8BRPp7_ZA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-12T11:06:17Z","receivedAt":"2019-03-12T11:06:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nOn 11/03/2019 17:54, Elijah Newren wrote:\n> A few other comments that I thought of while responding elsewhere in\n> the thread that didn't make sense to include elsewhere...\n> \n> On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>>\n>> +-m::\n>> +--merge::\n>> +       If you have local modifications to one or more files that are\n>> +       different between the current branch and the branch to which\n>> +       you are switching, the command refuses to switch branches in\n>> +       order to preserve your modifications in context.  However,\n>> +       with this option, a three-way merge between the current\n>> +       branch, your working tree contents, and the new branch is\n>> +       done, and you will be on the new branch.\n>> ++\n>> +When a merge conflict happens, the index entries for conflicting\n>> +paths are left unmerged, and you need to resolve the conflicts\n>> +and mark the resolved paths with `git add` (or `git rm` if the merge\n>> +should result in deletion of the path).\n> \n> Now that Phillip highlighted issues with -m and -f, it's hard not to\n> wonder about other corner cases.  For example, what if the user made\n> some changes, staged them, then made more changes, then tried to 'git\n> checkout -m <other branch>'?  That's no longer a three-way merge, but\n> four way.  How does that work?  Does it just rely on merge-recursive's\n> (poorly defined) choice of when to bail out and when to permit such\n> craziness?\n\nIf the two-way merge fails then it does 'git add -u' before calling \nmerge_recursive(), then any merged paths are reset to the new HEAD \n(which throws away newly added files, it should keep anything that is \nnot in HEAD or HEAD@{1}). So any staged changes are lost.\n\n>> +--orphan <new-branch>::\n>> +       Create a new 'orphan' branch, named `<new-branch>`, started from\n>> +       `<start-point>` and switch to it. See explanation of the same\n>> +       option in linkgit:git-checkout[1] for details.\n> \n> Sigh...does this mean --orphan will remain broken?  It has always\n> driven me crazy that it leaves you with a fully populated rather than\n> an empty index.\n\nI've always thought that was weird.\n\n> It seemed broken to me before I figured out the\n> special usecase,\n\nI haven't figured it out yet - what is it?\n\n\nBest Wishes\n\nPhillip\n\n> though it still seemed like the wrong default (an\n> empty index wouldn't surprise due to the \"orphan\" name, but a full one\n> does to those without the special usecase in mind).  Oh well, that's a\n> much smaller battle than the big picture of getting switch and restore\n> in place, and I don't want to derail the bigger picture; anything\n> using --orphan is a somewhat special case anyway.\n> \n>> +You can give the `-m` flag to the command, which would try a three-way\n>> +merge:\n>> +\n>> +------------\n>> +$ git switch -m mytopic\n>> +Auto-merging frotz\n>> +------------\n>> +\n>> +After this three-way merge, the local modifications are _not_\n>> +registered in your index file, so `git diff` would show you what\n>> +changes you made since the tip of the new branch.\n> \n> ...even if the local modifications were registered in the index file\n> before?  Is this why Phillip's \"x\" was missing and how it avoided\n> doing a four-way merge?  I guess it kinda makes sense, view from this\n> angle, but I'm not so sure I like it.  Hmm....\n> \n"},{"id":"371241","messageId":"CACsJy8CXX+Gk2SFGSULQ2TEzzXRdUr3vCxHxxsBOzhKCLE4YVQ@mail.gmail.com","threadId":"50360","inReplyTo":"f63a7ae7-0a08-02f2-23e6-4389d8fda729@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-12T11:54:38Z","receivedAt":"2019-03-12T11:55:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 12, 2019 at 12:03 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Duy\n>\n> On 11/03/2019 11:47, Duy Nguyen wrote:\n> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >>\n> >>\n> >> Hi Duy\n> >>\n> >> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> >>> \"git checkout\" doing too many things is a source of confusion for many\n> >>> users (and it even bites old timers sometimes). To remedy that, the\n> >>> command will be split into two new ones: switch and\n> >>> something-to-checkout-paths.\n> >>\n> >> I think this is a good idea, thanks for working on it. I wonder if it\n> >> would be a good idea to have the new command refuse to checkout a new\n> >> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> >> an option to override the check) as switching branches in the middle of\n> >> one of those is likely to be confusing to users (if I do it it is\n> >> normally because I've forgotten that I've not run 'git whatever\n> >> --continue').\n> >\n> > Interesting. I think this would be a good default if we have an escape\n> > hatch (which could even come later). I often wander off to some other\n> > branch and go back. But then half the time I end up forgetting I'm in\n> > a middle of something and just \"git rebase --quit\" :P\n> >\n> > Of course with git-stash (*) and git-worktree, I guess there's little\n> > reason to just switch away from a something-in-progress worktree. I'll\n> > try to implement this in the next round, unless someone objects.\n> >\n> > (*) I hope git-stash remembers and restores \"something-in-progress\"\n> > status. Dunno. Never used it much.\n>\n> I don't think it does. For rebase it's non trivial as it needs to\n> remember the refs under refs/rewritten and stop gc from collecting any\n> of them or the original head (in theory the todo list can contain\n> commits that the user has added from other branches as well so they'd\n> also need to be protected from gc). For cherry-pick there are gc issues\n> as well.\n\ngc issues should be fixed anyway because gc could start any time (even\nmanually by the user). And teaching pack-objects and friends to not\ndelete original head and the todo list does not look so hard.\n#leftovers stuff?\n-- \nDuy\n"},{"id":"371242","messageId":"CACsJy8Ax0nt1zt9eqNs6QM4gCS-PFfXH9woxd81X-_zywQRnVw@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BFStPOtA_OGrS3HCe_XqWN1roBnzss3nefcHdQg=9eYLQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-12T11:58:21Z","receivedAt":"2019-03-12T11:58:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > >\n> > > Hi Duy\n> > >\n> > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > > > \"git checkout\" doing too many things is a source of confusion for many\n> > > > users (and it even bites old timers sometimes). To remedy that, the\n> > > > command will be split into two new ones: switch and\n> > > > something-to-checkout-paths.\n> > >\n> > > I think this is a good idea, thanks for working on it. I wonder if it\n> > > would be a good idea to have the new command refuse to checkout a new\n> > > branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> > > an option to override the check) as switching branches in the middle of\n> > > one of those is likely to be confusing to users (if I do it it is\n> > > normally because I've forgotten that I've not run 'git whatever\n> > > --continue').\n> >\n> > Interesting. I think this would be a good default if we have an escape\n> > hatch (which could even come later). I often wander off to some other\n> > branch and go back. But then half the time I end up forgetting I'm in\n> > a middle of something and just \"git rebase --quit\" :P\n> >\n> > Of course with git-stash (*) and git-worktree, I guess there's little\n> > reason to just switch away from a something-in-progress worktree. I'll\n> > try to implement this in the next round, unless someone objects.\n>\n> No objection here; I like this idea.\n\nOne last thing. What about --detach? Should it have the same\nprotection or should we let the user doing --detach (experiments) take\nthe responsibity to not screw themselves up?\n-- \nDuy\n"},{"id":"371245","messageId":"CACsJy8DxOeKEAygiEa644pcQw-zTsnLyrtPL4sKWmPFEdNWpcQ@mail.gmail.com","threadId":"50360","inReplyTo":"d78392c3-7282-43ab-b88c-aa13fb5f937a@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-12T12:19:13Z","receivedAt":"2019-03-12T12:19:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > then it'd make sense to use --recreate instead.  But if you\n> > think some might adopt a workflow where they just use -C without first\n> > trying -c (\"create this branch, and I don't care if I made it before\n> > just create it here\"), then --force-create makes sense.\n> >\n> > Another option, possibly showing my lack of understanding why this\n> > flag was useful in the first place: just drop this set of flags from\n> > this command.  People can switch to the branch and then use reset\n> > --hard <startpoint>, right?  Or (if they don't care about the reflog,\n> > which they probably don't) delete the branch first and then recreate\n> > it?  Not sure why we need to give another way to do these operations.\n> > (In contrast, I see -c as being used frequently enough to have merit\n> > even if it could be implemented as two separate commands.)\n>\n> I tend to agree with this but that's probably because I don't really use\n> checkout -B. I'm not sure if it's widely used or not. I do find checkout\n> -b convenient though.\n\nYeah I think both -b and -B are about convenience.\n\nAnother point is multiple steps of switching and resetting could be\nreally painful for people with large worktrees. Doing everything in\none go is probably preferable because it generates less I/O.\n\nThe mention of \"git reset --hard\" triggers me because I secretly want\nto replace that command (\"git reset\", all modes), and in the last\n\"promotion\" patch, \"reset --hard\" is replaced with \"switch -C\" three\ntimes. We get really close to replacing git-reset if we have something\nto say \"-C <current branch>\". Whether this operation fits in the\n\"switching things\" mentality, I'm still not sure.\n\nBut I would not mind dropping -C for now, if people think it's not\nthat useful. We can bring it back in incremental updates if we realize\nwe miss it so much. I'll keep it unless somebody says something.\n\nPS. The same probably goes for --orphan too. Wait and see if people\ncomplain, then we know how they actually use it.\n-- \nDuy\n"},{"id":"371268","messageId":"CAPig+cRN_cKhu2UQSiJHOK-bgk+g7897_2Acb_kgNK7w6_fS5g@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8DxOeKEAygiEa644pcQw-zTsnLyrtPL4sKWmPFEdNWpcQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-12T15:36:50Z","receivedAt":"2019-03-12T15:37:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > I tend to agree with this but that's probably because I don't really use\n> > checkout -B. I'm not sure if it's widely used or not. I do find checkout\n> > -b convenient though.\n>\n> Yeah I think both -b and -B are about convenience.\n>\n> But I would not mind dropping -C for now, if people think it's not\n> that useful. We can bring it back in incremental updates if we realize\n> we miss it so much. I'll keep it unless somebody says something.\n\nIt's not much of a datapoint, but I do use \"git checkout -B\" (and\ntherefore would use \"git switch -C\") periodically (in addition to\n-b/-c, which I use all the time). And, convenience is important,\nespecially considering that \"git switch\" is already more painful in\nsome ways than \"git checkout\", due to having to trigger and spell out\ncertain things explicitly (such as detaching).\n\n> PS. The same probably goes for --orphan too. Wait and see if people\n> complain, then we know how they actually use it.\n\nAgain, not much of a datapoint, but I do use --orphan periodically.\nThe idea of \"fixing\" the behavior so that --orphan starts with a clean\nslate is certainly appealing (since it matches how I've used orphan\nbranches in each case).\n"},{"id":"371270","messageId":"CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com","threadId":"50360","inReplyTo":"ca6f8e4b-d60c-877c-52e7-fb91c99cd3a5@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-12T16:43:21Z","receivedAt":"2019-03-12T16:43:37Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 12, 2019 at 4:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Elijah\n>\n> On 11/03/2019 17:54, Elijah Newren wrote:\n> > A few other comments that I thought of while responding elsewhere in\n> > the thread that didn't make sense to include elsewhere...\n> >\n> > On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >>\n> >> +-m::\n> >> +--merge::\n> >> +       If you have local modifications to one or more files that are\n> >> +       different between the current branch and the branch to which\n> >> +       you are switching, the command refuses to switch branches in\n> >> +       order to preserve your modifications in context.  However,\n> >> +       with this option, a three-way merge between the current\n> >> +       branch, your working tree contents, and the new branch is\n> >> +       done, and you will be on the new branch.\n> >> ++\n> >> +When a merge conflict happens, the index entries for conflicting\n> >> +paths are left unmerged, and you need to resolve the conflicts\n> >> +and mark the resolved paths with `git add` (or `git rm` if the merge\n> >> +should result in deletion of the path).\n> >\n> > Now that Phillip highlighted issues with -m and -f, it's hard not to\n> > wonder about other corner cases.  For example, what if the user made\n> > some changes, staged them, then made more changes, then tried to 'git\n> > checkout -m <other branch>'?  That's no longer a three-way merge, but\n> > four way.  How does that work?  Does it just rely on merge-recursive's\n> > (poorly defined) choice of when to bail out and when to permit such\n> > craziness?\n>\n> If the two-way merge fails then it does 'git add -u' before calling\n> merge_recursive(), then any merged paths are reset to the new HEAD\n> (which throws away newly added files, it should keep anything that is\n> not in HEAD or HEAD@{1}). So any staged changes are lost.\n\nAh, so roughly\n  * git add -u\n  * uncommitted_tree=$(git write-tree)\n  * git reset --hard\n  * git checkout $other_branch\n  * git merge-recursive $old_branch -- $other_branch $uncommitted_tree\n  * git reset --mixed HEAD\n\nThis at least gives well defined behavior, even if somewhat suboptimal\nin relation to losing staged changes (especially when those staged\nchanges were new files).\n\nI wonder if it'd be nicer, after I get my don't-touch-the-working-tree\nmerge rewrite done, to instead do something like:\n  * Write the beginning index to a tree; call it $tree_0\n  * Note whether any working tree files differ from the index, add\nthese all to a temporary index and write to to a tree; call it\n$tree_1.\n  * Do a three way in-memory merge of $old_branch with $other_branch\nand $tree_0; call it $merged_tree if there are no conflicts\n  * If $tree_0 == $tree_1, checkout the new branch and update the\nindex and working tree to reflect the merge result.\n  * If $tree_0 != $tree_1 and there were any conflicts, abort telling\nthe user they need to either unstage or stage changes first (we don't\nwant to confuse users with a merge of a merge).\n  * Switch to the new branch, and update the index to match $merged_tree\n  * Do a three way in-memory merge of $old_branch with $merged_tree\nand $tree_1, writing the results (including any conflicts) to the\nworking tree afterward.\n\nPros of this method:\n  * We don't lose newly staged files\n  * We don't lose user's carefully staged entries for existing files either\nCons of this method:\n  * It may abort with an error if the user has a mix of both staged\nand unstaged changes (in particular, it will do so if the user's\nstaged changes conflict with some difference in the new branch)\n\n\nThoughts?\n\n>\n> >> +--orphan <new-branch>::\n> >> +       Create a new 'orphan' branch, named `<new-branch>`, started from\n> >> +       `<start-point>` and switch to it. See explanation of the same\n> >> +       option in linkgit:git-checkout[1] for details.\n> >\n> > Sigh...does this mean --orphan will remain broken?  It has always\n> > driven me crazy that it leaves you with a fully populated rather than\n> > an empty index.\n>\n> I've always thought that was weird.\n>\n> > It seemed broken to me before I figured out the\n> > special usecase,\n>\n> I haven't figured it out yet - what is it?\n\nIt's a presumption that despite the fact that you want a new branch,\nand one with no history to boot, that for some reason you want all the\nprevious branch's current contents.  In particular, you can think of\nit as a way to squash all the history of an existing branch into a\nsingle commit in a new branch.\n\nKnowing of this usecase doesn't make it bother me any less when I want\nto create a new unrelated empty branch; it seems like it took the\nesoteric usecase over the common one to me, but I'm biased.  It makes\nme feel better than neither you nor Eric could understand this\nbehavior of --orphan either.\n"},{"id":"371272","messageId":"CABPp-BHpoa+tNW=7+6TOmoQTvbap3iavqApEGny4E-rwv1gZVQ@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cRN_cKhu2UQSiJHOK-bgk+g7897_2Acb_kgNK7w6_fS5g@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-12T16:51:13Z","receivedAt":"2019-03-12T16:51:28Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > > I tend to agree with this but that's probably because I don't really use\n> > > checkout -B. I'm not sure if it's widely used or not. I do find checkout\n> > > -b convenient though.\n> >\n> > Yeah I think both -b and -B are about convenience.\n> >\n> > But I would not mind dropping -C for now, if people think it's not\n> > that useful. We can bring it back in incremental updates if we realize\n> > we miss it so much. I'll keep it unless somebody says something.\n>\n> It's not much of a datapoint, but I do use \"git checkout -B\" (and\n> therefore would use \"git switch -C\") periodically (in addition to\n> -b/-c, which I use all the time). And, convenience is important,\n> especially considering that \"git switch\" is already more painful in\n> some ways than \"git checkout\", due to having to trigger and spell out\n> certain things explicitly (such as detaching).\n\nOoh, interesting.  I haven't used it and didn't know who did, but\nsince you do you can probably answer the question surrounding the\nlong-name for the -C option from earlier in the thread:\n\nDo you use checkout -B only when checkout -b fails, or do you use it\npre-emptively?  The former would suggest we should use a name like\n--recreate, while the latter would suggest a name more like\n--force-create.\n\n> > PS. The same probably goes for --orphan too. Wait and see if people\n> > complain, then we know how they actually use it.\n>\n> Again, not much of a datapoint, but I do use --orphan periodically.\n> The idea of \"fixing\" the behavior so that --orphan starts with a clean\n> slate is certainly appealing (since it matches how I've used orphan\n> branches in each case).\n\nThe only three people who have commented on --orphan in this thread\nall apparently feel the same way: the current behavior is wrong.\nMaybe we can switch it to start with an empty index after all?\n"},{"id":"371274","messageId":"CABPp-BFw2O_N8bYz8DDSj5RWhn-TNh7Z8ww4joKYvL4CQrOO4g@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8Ax0nt1zt9eqNs6QM4gCS-PFfXH9woxd81X-_zywQRnVw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-12T17:05:06Z","receivedAt":"2019-03-12T17:05:22Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 12, 2019 at 4:58 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:\n> > On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> > > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > > > > \"git checkout\" doing too many things is a source of confusion for many\n> > > > > users (and it even bites old timers sometimes). To remedy that, the\n> > > > > command will be split into two new ones: switch and\n> > > > > something-to-checkout-paths.\n> > > >\n> > > > I think this is a good idea, thanks for working on it. I wonder if it\n> > > > would be a good idea to have the new command refuse to checkout a new\n> > > > branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> > > > an option to override the check) as switching branches in the middle of\n> > > > one of those is likely to be confusing to users (if I do it it is\n> > > > normally because I've forgotten that I've not run 'git whatever\n> > > > --continue').\n> > >\n> > > Interesting. I think this would be a good default if we have an escape\n> > > hatch (which could even come later). I often wander off to some other\n> > > branch and go back. But then half the time I end up forgetting I'm in\n> > > a middle of something and just \"git rebase --quit\" :P\n> > >\n> > > Of course with git-stash (*) and git-worktree, I guess there's little\n> > > reason to just switch away from a something-in-progress worktree. I'll\n> > > try to implement this in the next round, unless someone objects.\n> >\n> > No objection here; I like this idea.\n>\n> One last thing. What about --detach? Should it have the same\n> protection or should we let the user doing --detach (experiments) take\n> the responsibity to not screw themselves up?\n\nMy intuition here is a bit weaker; it would be nice to hear others'\nopinions.  My best guess thinking it over a bit is that while someone\ndoing --detach is more likely to know what they are doing than other\nusers of the 'switch' command, they may also be the kind of person who\nis more likely to run interactive rebases and thus had more\nopportunities to forget that they are still in the middle of one.  I\nthink I've been guilty of that a few times.  So, while this would be\nless critical for this case, I lean towards saying that it may still\nbe helpful anyway, and if nothing else the consistency of handling all\nswitching cases the same seems beneficial.\n"},{"id":"371277","messageId":"CAPig+cSe-v-GU=UG_PyUjf9itz_Ddqq4-Ni4h=2TtbLDXr5WJA@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BHpoa+tNW=7+6TOmoQTvbap3iavqApEGny4E-rwv1gZVQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-12T17:28:35Z","receivedAt":"2019-03-12T17:28:51Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Mar 12, 2019 at 12:51 PM Elijah Newren <newren@gmail.com> wrote:\n> On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > It's not much of a datapoint, but I do use \"git checkout -B\" (and\n> > therefore would use \"git switch -C\") periodically (in addition to\n> > -b/-c, which I use all the time). And, convenience is important,\n> > especially considering that \"git switch\" is already more painful in\n> > some ways than \"git checkout\", due to having to trigger and spell out\n> > certain things explicitly (such as detaching).\n>\n> Ooh, interesting.  I haven't used it and didn't know who did, but\n> since you do you can probably answer the question surrounding the\n> long-name for the -C option from earlier in the thread:\n>\n> Do you use checkout -B only when checkout -b fails, or do you use it\n> pre-emptively?  The former would suggest we should use a name like\n> --recreate, while the latter would suggest a name more like\n> --force-create.\n\nIt doesn't come up often, but I use \"git checkout -B\" when I know that\nI want to start an existing branch over from scratch to build upon\nsome unrelated branch. This almost always happens when I'm programming\nexperimentally. I'll create a new branch with the desired _final_\nname, start work on it, realize that some other approach might be\nbetter, so set that aside and create a new branch with some junk name,\nwork on that for a while, possibly repeat, creating more branches with\nmore junk names, etc. Sometimes the result is that bits a pieces of\nthe various junk-named branches can be pulled together into a final\nproduct, in which case I may \"git checkout -B <final>\", overwriting\nthe first branch I created, and then pull a few things from other\njunk-named branches to reach the end. (More often, though, one of the\njunk-named branches ends up being what I want to go with, so I just\n\"git branch -M <final>\", throwing away the original branch, and\nkeeping the junk-named one but with the _final_ name). Sorry for the\ntoo long-winded explanation.\n\nI think --force-create is a better name because \"force\" implies\nstrongly that you're doing something potentially dangerous. Also,\nevery option in git-branch which deletes or overwrites a branch name\nrequires \"force\", so it's good precedent to follow.\n\n> > Again, not much of a datapoint, but I do use --orphan periodically.\n> > The idea of \"fixing\" the behavior so that --orphan starts with a clean\n> > slate is certainly appealing (since it matches how I've used orphan\n> > branches in each case).\n>\n> The only three people who have commented on --orphan in this thread\n> all apparently feel the same way: the current behavior is wrong.\n> Maybe we can switch it to start with an empty index after all?\n\nStarting empty may match intuition better. (More importantly, perhaps,\nit's harder to come up with a use-case for --orphan which doesn't\ninvolve starting with a clean slate.)\n"},{"id":"371319","messageId":"xmqqo96ffsgs.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CAPig+cSe-v-GU=UG_PyUjf9itz_Ddqq4-Ni4h=2TtbLDXr5WJA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-13T01:55:47Z","receivedAt":"2019-03-13T01:55:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> Do you use checkout -B only when checkout -b fails, or do you use it\n>> pre-emptively?  The former would suggest we should use a name like\n>> --recreate, while the latter would suggest a name more like\n>> --force-create.\n>\n> It doesn't come up often, but I use \"git checkout -B\" when I know that\n> I want to start an existing branch over from scratch to build upon\n> some unrelated branch.\n\nFWIW, I always use \"checkout -B\" at least twice during an\nintegration cycle.  \"git checkout -B jch master\" before rebuilding\nmy private edition with select topics (including all the topics\nalready in 'next'), plus \"git checkout -B pu jch\" before rebuilding\nthe 'pu' branch with the other topics I happen to have looked at.\nThese are similar to your use case---the branches have constant\n\"purpose\" and always exist, but they are rebuilt from scratch when\nthey get updated.\n\n"},{"id":"371348","messageId":"CACsJy8BkdUN=KNGxRi-4jGcw3EQTAuu_2eKcNRDv0uwP5L4=Nw@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BEBudobnduipQrSvyQWQ7Hb4WmmoptCQ+FyY8BRPp7_ZA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-13T11:05:22Z","receivedAt":"2019-03-13T11:05:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 12, 2019 at 12:54 AM Elijah Newren <newren@gmail.com> wrote:\n> > +--progress::\n> > +--no-progress::\n> > +       Progress status is reported on the standard error stream\n> > +       by default when it is attached to a terminal, unless `--quiet`\n> > +       is specified. This flag enables progress reporting even if not\n> > +       attached to a terminal, regardless of `--quiet`.\n>\n> This again makes me curious what --quiet actually supresses; in the\n> case of branch switching, are there any non-warning informational\n> messages other than progress reports that are printed?\n\nOne big thing git-checkout and git-switch will print when not --quiet\nis \"git diff --name-status\" to highlight local changes since we allow\nswitching branches when the worktree is not clean. Should it be\nmentioned in --quiet description?\n\nWe could also occasionally print advice, branch tracking info, and\none-liner summary like \"Switched to (new) branch 'master'\".\n-- \nDuy\n"},{"id":"371367","messageId":"CABPp-BG8cW0mbyM7zboR8rLWae2Yq7TXuGBDYSmY4synA9KMDQ@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8BkdUN=KNGxRi-4jGcw3EQTAuu_2eKcNRDv0uwP5L4=Nw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-13T14:36:01Z","receivedAt":"2019-03-13T14:36:18Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Mar 13, 2019 at 4:05 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Mar 12, 2019 at 12:54 AM Elijah Newren <newren@gmail.com> wrote:\n> > > +--progress::\n> > > +--no-progress::\n> > > +       Progress status is reported on the standard error stream\n> > > +       by default when it is attached to a terminal, unless `--quiet`\n> > > +       is specified. This flag enables progress reporting even if not\n> > > +       attached to a terminal, regardless of `--quiet`.\n> >\n> > This again makes me curious what --quiet actually supresses; in the\n> > case of branch switching, are there any non-warning informational\n> > messages other than progress reports that are printed?\n>\n> One big thing git-checkout and git-switch will print when not --quiet\n> is \"git diff --name-status\" to highlight local changes since we allow\n> switching branches when the worktree is not clean. Should it be\n> mentioned in --quiet description?\n\nNah, that all makes sense.  It was more a curiosity (I tend to have a\nclean working tree most the time when switching branches, I guess;\nmaybe I also filtered out messages I was used to seeing and couldn't\nremember seeing them) and also I had a small concern that perhaps this\nbit of documentation was relevant for one part of git-checkout and not\nthe other, and thus shouldn't appear in both git-switch and\ngit-restore.\n\n> We could also occasionally print advice, branch tracking info, and\n> one-liner summary like \"Switched to (new) branch 'master'\".\n\nThanks for the explanations.  This all makes sense.  However, all\nthese answers are specific to git-switch; perhaps the detailed\nexplanation of --progress can be shorted for git-restore?  Or does it\ntoo have reasons to print non-progress informational messages?\n"},{"id":"371388","messageId":"20190313183604.GA27503@esm","threadId":"50360","inReplyTo":"20190308095752.8574-18-pclouds@gmail.com","subject":"Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Eckhard Maaß","fromEmail":"eckhard.s.maass@googlemail.com","sentAt":"2019-03-13T18:36:04Z","receivedAt":"2019-03-13T18:36:10Z","isPatch":true,"sender":{"key":"eckhard.s.maass@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/21984134?v=4"},"body":"On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> Similar to automatic detach, this behavior could be confusing because\n> it can sometimes create a new branch without a user asking it to,\n> especially when the user is still not aware about this feature.\n> \n> In the future, perhaps we could have a config key to disable these\n> safety nets and let 'switch' do automatic detach or dwim\n> again. But that will be opt-in after the user knows what is what. For\n> now give a short option if you want to use it often.\n\nAs I am late to the patch series (sorry!), has there been already any\ndiscussion on that? In my experience, people get confused with detached\nHEAD state quite often, whereas the automatic creation of a local branch\nis no problem. So if it is deemed to be too confusing to dwim in this\ncase, could we add a hint suggesting the command? Something like:\n\"No suitable branch <foo> found, however there is a remote tracking\nbranch <origin/foo> that you can siwtch and create with `git switch\n--guess foo`\" (or maybe the one without guess)?\n\nAnd while at it - what should happen, if:\n\n- there is a tag named example\n- no local branch example\n- a branch at origin/example\n\n... and we switch then? Right now it just gives \"cannot find branch\",\nshould there be more information? Should it even create a branch\nexample? With switch, switching a branch is unambiguous, even though\nthere is a tag with that name. If I really want to --guess - should I be\ngiven more information?\n\nGreetings,\nEckhard\n"},{"id":"371449","messageId":"CACsJy8CuNQR7javX7KTC7txivH1OkejCbVJdHGD+XJ-0ftT3ag@mail.gmail.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T03:29:09Z","receivedAt":"2019-03-14T03:29:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > +-m::\n> > +--merge::\n> > +     If you have local modifications to one or more files that are\n> > +     different between the current branch and the branch to which\n> > +     you are switching, the command refuses to switch branches in\n> > +     order to preserve your modifications in context.  However,\n> > +     with this option, a three-way merge between the current\n> > +     branch, your working tree contents, and the new branch is\n> > +     done, and you will be on the new branch.\n>\n> I was wondering what people felt about making this the default for the\n> new command. If I'm carrying changes over to the new branch then I want\n> them to be merged in, it's annoying to have them carried over most of\n> the time but then sometimes have the checkout fail without specifying\n> --merge.\n\nIf my worktree has local changes and I accidentally switch to a\ndifferent branch, I could switch back without losing any local changes\nand the tree I have is exactly what I had before the switch. Is this\nstill true if -m is made default?\n\nI think sometimes a 3-way merge creates marker conflicts in file, and\nthis does not look easy to reverse when switching back. If it's true\nand we can detect it, we can still abort this case (i.e. requiring -m\nto continue) while allowing succesful 3-way merges by default. But are\nsuccessful 3-way merges reversible?\n-- \nDuy\n"},{"id":"371454","messageId":"xmqqr2ba9ii3.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-14T04:39:48Z","receivedAt":"2019-03-14T04:39:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> +-m::\n>> +--merge::\n>> +\tIf you have local modifications to one or more files that are\n>> +\tdifferent between the current branch and the branch to which\n>> +\tyou are switching, the command refuses to switch branches in\n>> +\torder to preserve your modifications in context.  However,\n>> +\twith this option, a three-way merge between the current\n>> +\tbranch, your working tree contents, and the new branch is\n>> +\tdone, and you will be on the new branch.\n>\n> I was wondering what people felt about making this the default for the\n> new command.\n\nEven its inventor (me) never runs \"-m\" without first seeing checkout\nwithout \"-m\" fail, and seeing how much can potentially be lost if\nthe branch switch conflicts by asking \"git diff\" (no other\noptions).  It is very unadvisable to make it the default.\n"},{"id":"371464","messageId":"CABPp-BHB3c0XqS57Do8=YFtbVyBEe26kbJtgDxWAaWAMzsgdoA@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8CuNQR7javX7KTC7txivH1OkejCbVJdHGD+XJ-0ftT3ag@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-14T05:59:20Z","receivedAt":"2019-03-14T05:59:34Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Mar 13, 2019 at 8:29 PM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > > +-m::\n> > > +--merge::\n> > > +     If you have local modifications to one or more files that are\n> > > +     different between the current branch and the branch to which\n> > > +     you are switching, the command refuses to switch branches in\n> > > +     order to preserve your modifications in context.  However,\n> > > +     with this option, a three-way merge between the current\n> > > +     branch, your working tree contents, and the new branch is\n> > > +     done, and you will be on the new branch.\n> >\n> > I was wondering what people felt about making this the default for the\n> > new command. If I'm carrying changes over to the new branch then I want\n> > them to be merged in, it's annoying to have them carried over most of\n> > the time but then sometimes have the checkout fail without specifying\n> > --merge.\n>\n> If my worktree has local changes and I accidentally switch to a\n> different branch, I could switch back without losing any local changes\n> and the tree I have is exactly what I had before the switch. Is this\n> still true if -m is made default?\n>\n> I think sometimes a 3-way merge creates marker conflicts in file, and\n> this does not look easy to reverse when switching back. If it's true\n> and we can detect it, we can still abort this case (i.e. requiring -m\n\nYou cannot yet do this with merge_recursive; it writes conflicts to\nthe worktree as it goes and doesn't tell you whether the merge was\nsuccessful or had conflicts until its done.  So this would be very\ndangerous.  We'd first need a way to do an in-memory merge that\ndoesn't touch the working tree or index and which gives you the\nopportunity to check whether that worked before proceeding to write\nout any updates.  That is work I plan to do (for other reasons), but\nnot in progress currently.\n\n> to continue) while allowing succesful 3-way merges by default. But are\n> successful 3-way merges reversible?\n\nHmm, interesting.  So, switching from branch A to B, you start with\nlocal changes on top of A that could represent a virtual commit C.\nSo, you three-way merge C & B using A as the base.  It's clean, so we\nhave a new endpoint, call it D.  Switching back to A with the --merge\nflag would mean a three-way merge of D & A using B as the base.\n\nBut it might be easier if I re-labelled all of these with different\nterms. Let's say we squash all changes from A to B into a single\ncommit on top of A that we call commit B.  Check out C (a commit\nrepresenting your local changes on top of A), and cherry-pick B; that\nwill three-way merge C & B using A as the base.  It's clean, so we\nhave a new commit, call it D.  Now we tell git to revert B, which will\ndo a three-way merge of D (current HEAD) & A (parent of B), using B as\nthe base.  So, essentially, your question about reversibility boils\ndown to: if you can cleanly cherry-pick a commit to your current\nbranch, can you then immediately revert that same commit on top to\nreturn to where you were before?\n\n(Off the cuff, I _think_ the answer to that is yes due to the\nassumption that the cherry-pick was without conflicts, but I'm not\nimmediately sure.)\n"},{"id":"371468","messageId":"xmqqsgvp9axu.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CABPp-BHB3c0XqS57Do8=YFtbVyBEe26kbJtgDxWAaWAMzsgdoA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-14T07:23:09Z","receivedAt":"2019-03-14T07:23:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n>> I think sometimes a 3-way merge creates marker conflicts in file, and\n>> this does not look easy to reverse when switching back. If it's true\n>> and we can detect it, we can still abort this case (i.e. requiring -m\n\nPaths with actual conflicts are much easier to recover from than\npaths that cleanly merge.   You have your original in stage #2, so\nyou should be able to \"restore-path --stage=2 --from-stage path...\"\nthem.  Once the contents are auto-resolved cleanly, however, the\ncached contents are automaticaly updated to the auto-resolved result,\nand it needs more work to reverse the effect of the merge (it is\ndoable, of course, as you know exactly the contents of the\nswitched-to branch and the contents of the switching-from branch, so\nit is just the matter of running 3-way merge in the right direction\nto recover what used to be in the working tree).\n"},{"id":"371470","messageId":"20190314091753.GA9852@ash","threadId":"50360","inReplyTo":"CAPig+cSe-v-GU=UG_PyUjf9itz_Ddqq4-Ni4h=2TtbLDXr5WJA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T09:17:53Z","receivedAt":"2019-03-14T09:18:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:\n> > > Again, not much of a datapoint, but I do use --orphan periodically.\n> > > The idea of \"fixing\" the behavior so that --orphan starts with a clean\n> > > slate is certainly appealing (since it matches how I've used orphan\n> > > branches in each case).\n> >\n> > The only three people who have commented on --orphan in this thread\n> > all apparently feel the same way: the current behavior is wrong.\n> > Maybe we can switch it to start with an empty index after all?\n> \n> Starting empty may match intuition better. (More importantly, perhaps,\n> it's harder to come up with a use-case for --orphan which doesn't\n> involve starting with a clean slate.)\n\nOK so the new --orphan description would be like this, right?\n\n--8<--\n--orphan <new-branch>::\n\tCreate a new 'orphan' branch, named `<new-branch>`. If\n\t`<start-point>` is specified, the working tree is adjusted to\n\tmatch it. The index remains empty (i.e. no file is tracked).\n-->8--\n\nI was wondering if instead of the empty index, we mark on files from\n<start-point> as intent-to-add. That way \"git commit -a\" achieves the\nsame as before, but you could still carefully craft the new index and\n\"git commit\". Dunno. Not going to implement it unless somebody says\nsomething, since I rarely (if ever?) use --orphan.\n\nI may need someone to come up with a convincing commit message\ntoo. All I've got is \"I've been told this is a good thing to do\" :)\n--\nDuy\n"},{"id":"371474","messageId":"1f52d046-11a4-c1af-32f6-d0de5c358d55@gmail.com","threadId":"50360","inReplyTo":"CABPp-BFw2O_N8bYz8DDSj5RWhn-TNh7Z8ww4joKYvL4CQrOO4g@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-14T10:42:06Z","receivedAt":"2019-03-14T10:42:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 12/03/2019 17:05, Elijah Newren wrote:\n> On Tue, Mar 12, 2019 at 4:58 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>> On Tue, Mar 12, 2019 at 12:25 AM Elijah Newren <newren@gmail.com> wrote:\n>>> On Mon, Mar 11, 2019 at 4:47 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>>>> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> \n>>>>> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n>>>>>> \"git checkout\" doing too many things is a source of confusion for many\n>>>>>> users (and it even bites old timers sometimes). To remedy that, the\n>>>>>> command will be split into two new ones: switch and\n>>>>>> something-to-checkout-paths.\n>>>>>\n>>>>> I think this is a good idea, thanks for working on it. I wonder if it\n>>>>> would be a good idea to have the new command refuse to checkout a new\n>>>>> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n>>>>> an option to override the check) as switching branches in the middle of\n>>>>> one of those is likely to be confusing to users (if I do it it is\n>>>>> normally because I've forgotten that I've not run 'git whatever\n>>>>> --continue').\n>>>>\n>>>> Interesting. I think this would be a good default if we have an escape\n>>>> hatch (which could even come later). I often wander off to some other\n>>>> branch and go back. But then half the time I end up forgetting I'm in\n>>>> a middle of something and just \"git rebase --quit\" :P\n>>>>\n>>>> Of course with git-stash (*) and git-worktree, I guess there's little\n>>>> reason to just switch away from a something-in-progress worktree. I'll\n>>>> try to implement this in the next round, unless someone objects.\n>>>\n>>> No objection here; I like this idea.\n>>\n>> One last thing. What about --detach? Should it have the same\n>> protection or should we let the user doing --detach (experiments) take\n>> the responsibity to not screw themselves up?\n> \n> My intuition here is a bit weaker; it would be nice to hear others'\n> opinions.  My best guess thinking it over a bit is that while someone\n> doing --detach is more likely to know what they are doing than other\n> users of the 'switch' command, they may also be the kind of person who\n> is more likely to run interactive rebases and thus had more\n> opportunities to forget that they are still in the middle of one.  I\n> think I've been guilty of that a few times.  So, while this would be\n> less critical for this case, I lean towards saying that it may still\n> be helpful anyway, and if nothing else the consistency of handling all\n> switching cases the same seems beneficial.\n> I think that having all the switching cases consistent makes sense as it\nis easy to understand and avoids complications if we end up adding a\nconfig option to allow an implicit --detach in the future as suggested\nin one of the later patches.\n\nBest Wishes\n\nPhillip\n"},{"id":"371475","messageId":"78628256-79dc-3036-c57b-a96797ceb120@gmail.com","threadId":"50360","inReplyTo":"CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-14T11:00:42Z","receivedAt":"2019-03-14T11:00:48Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 12/03/2019 16:43, Elijah Newren wrote:\n> On Tue, Mar 12, 2019 at 4:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> Hi Elijah\n>>\n>> On 11/03/2019 17:54, Elijah Newren wrote:\n>>> A few other comments that I thought of while responding elsewhere in\n>>> the thread that didn't make sense to include elsewhere...\n>>>\n>>> On Fri, Mar 8, 2019 at 2:00 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>>>>\n>>>> +-m::\n>>>> +--merge::\n>>>> +       If you have local modifications to one or more files that are\n>>>> +       different between the current branch and the branch to which\n>>>> +       you are switching, the command refuses to switch branches in\n>>>> +       order to preserve your modifications in context.  However,\n>>>> +       with this option, a three-way merge between the current\n>>>> +       branch, your working tree contents, and the new branch is\n>>>> +       done, and you will be on the new branch.\n>>>> ++\n>>>> +When a merge conflict happens, the index entries for conflicting\n>>>> +paths are left unmerged, and you need to resolve the conflicts\n>>>> +and mark the resolved paths with `git add` (or `git rm` if the merge\n>>>> +should result in deletion of the path).\n>>>\n>>> Now that Phillip highlighted issues with -m and -f, it's hard not to\n>>> wonder about other corner cases.  For example, what if the user made\n>>> some changes, staged them, then made more changes, then tried to 'git\n>>> checkout -m <other branch>'?  That's no longer a three-way merge, but\n>>> four way.  How does that work?  Does it just rely on merge-recursive's\n>>> (poorly defined) choice of when to bail out and when to permit such\n>>> craziness?\n>>\n>> If the two-way merge fails then it does 'git add -u' before calling\n>> merge_recursive(), then any merged paths are reset to the new HEAD\n>> (which throws away newly added files, it should keep anything that is\n>> not in HEAD or HEAD@{1}). So any staged changes are lost.\n> \n> Ah, so roughly\n>   * git add -u\n>   * uncommitted_tree=$(git write-tree)\n>   * git reset --hard\n>   * git checkout $other_branch\n>   * git merge-recursive $old_branch -- $other_branch $uncommitted_tree\n>   * git reset --mixed HEAD\n\nSomething like that (I think it skips the reset and checkout and does\ngit merge-recursive $old_branch -- $uncommitted_tree $other_branch\nand then updating HEAD)\n\n> This at least gives well defined behavior, even if somewhat suboptimal\n> in relation to losing staged changes (especially when those staged\n> changes were new files).\n> \n> I wonder if it'd be nicer, after I get my don't-touch-the-working-tree\n> merge rewrite done, to instead do something like:\n>   * Write the beginning index to a tree; call it $tree_0\n>   * Note whether any working tree files differ from the index, add\n> these all to a temporary index and write to to a tree; call it\n> $tree_1.\n>   * Do a three way in-memory merge of $old_branch with $other_branch\n> and $tree_0; call it $merged_tree if there are no conflicts\n>   * If $tree_0 == $tree_1, checkout the new branch and update the\n> index and working tree to reflect the merge result.\n>   * If $tree_0 != $tree_1 and there were any conflicts, abort telling\n> the user they need to either unstage or stage changes first (we don't\n> want to confuse users with a merge of a merge).\n>   * Switch to the new branch, and update the index to match $merged_tree\n>   * Do a three way in-memory merge of $old_branch with $merged_tree\n> and $tree_1, writing the results (including any conflicts) to the\n> working tree afterward.\n\nAs much as it annoys me to have to clear conflicts from the index after\na `checkout` or `stash pop` I'm wary of updating the working tree with\nconflicts without marking those paths as unmerged in the index. Marking\nthem prevents the user from accidentally committing files with\nunresolved conflicts. It is also easier for the user to find the\nconflicts if they're marked as unmerged in the index, they can use `diff\n--cc` and recreate them if they need to start over with the resolution.\n\n> \n> Pros of this method:\n>   * We don't lose newly staged files\n>   * We don't lose user's carefully staged entries for existing files either\n> Cons of this method:\n>   * It may abort with an error if the user has a mix of both staged\n> and unstaged changes (in particular, it will do so if the user's\n> staged changes conflict with some difference in the new branch)\n\nI think it's a good way of preserving any unstaged changes, it is\nprobably good that the user is warned ahead of time that their staged\nchanges would be lost.\n\n> Thoughts?\n> \n>>\n>>>> +--orphan <new-branch>::\n>>>> +       Create a new 'orphan' branch, named `<new-branch>`, started from\n>>>> +       `<start-point>` and switch to it. See explanation of the same\n>>>> +       option in linkgit:git-checkout[1] for details.\n>>>\n>>> Sigh...does this mean --orphan will remain broken?  It has always\n>>> driven me crazy that it leaves you with a fully populated rather than\n>>> an empty index.\n>>\n>> I've always thought that was weird.\n>>\n>>> It seemed broken to me before I figured out the\n>>> special usecase,\n>>\n>> I haven't figured it out yet - what is it?\n> \n> It's a presumption that despite the fact that you want a new branch,\n> and one with no history to boot, that for some reason you want all the\n> previous branch's current contents.  In particular, you can think of\n> it as a way to squash all the history of an existing branch into a\n> single commit in a new branch.\n> \n> Knowing of this usecase doesn't make it bother me any less when I want\n> to create a new unrelated empty branch; it seems like it took the\n> esoteric usecase over the common one to me, but I'm biased.  It makes\n> me feel better than neither you nor Eric could understand this\n> behavior of --orphan either.\n\nThanks for the explanation, I agree it seems like a more esoteric use case.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"371476","messageId":"5cd15ff0-8913-9b92-6ddb-94eff6f22077@gmail.com","threadId":"50360","inReplyTo":"20190314091753.GA9852@ash","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-14T11:02:49Z","receivedAt":"2019-03-14T11:02:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 14/03/2019 09:17, Duy Nguyen wrote:\n> On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:\n>>>> Again, not much of a datapoint, but I do use --orphan periodically.\n>>>> The idea of \"fixing\" the behavior so that --orphan starts with a clean\n>>>> slate is certainly appealing (since it matches how I've used orphan\n>>>> branches in each case).\n>>>\n>>> The only three people who have commented on --orphan in this thread\n>>> all apparently feel the same way: the current behavior is wrong.\n>>> Maybe we can switch it to start with an empty index after all?\n>>\n>> Starting empty may match intuition better. (More importantly, perhaps,\n>> it's harder to come up with a use-case for --orphan which doesn't\n>> involve starting with a clean slate.)\n> \n> OK so the new --orphan description would be like this, right?\n> \n> --8<--\n> --orphan <new-branch>::\n> \tCreate a new 'orphan' branch, named `<new-branch>`. If\n> \t`<start-point>` is specified, the working tree is adjusted to\n> \tmatch it. The index remains empty (i.e. no file is tracked).\n> -->8--\n\nWhat happens if no <start-point> is given? Do you end up with an empty\nworking tree or the current one? I'd lean towards an empty working tree\n(with a check that there are no uncommitted changes, users can use\n`restore` if they want some of the files back) but that is inconsistent\nwith the implicit <start-point> of -c.\n\nBest Wishes\n\nPhillip\n\n> I was wondering if instead of the empty index, we mark on files from\n> <start-point> as intent-to-add. That way \"git commit -a\" achieves the\n> same as before, but you could still carefully craft the new index and\n> \"git commit\". Dunno. Not going to implement it unless somebody says\n> something, since I rarely (if ever?) use --orphan.\n> \n> I may need someone to come up with a convincing commit message\n> too. All I've got is \"I've been told this is a good thing to do\" :)\n> --\n> Duy\n> \n\n"},{"id":"371493","messageId":"CACsJy8C54dndO_VoMZhvY=N3_prpLESp3w5NceUgRLPvnBVGLQ@mail.gmail.com","threadId":"50360","inReplyTo":"5cd15ff0-8913-9b92-6ddb-94eff6f22077@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T12:56:58Z","receivedAt":"2019-03-14T12:57:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 14, 2019 at 6:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 14/03/2019 09:17, Duy Nguyen wrote:\n> > On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:\n> >>>> Again, not much of a datapoint, but I do use --orphan periodically.\n> >>>> The idea of \"fixing\" the behavior so that --orphan starts with a clean\n> >>>> slate is certainly appealing (since it matches how I've used orphan\n> >>>> branches in each case).\n> >>>\n> >>> The only three people who have commented on --orphan in this thread\n> >>> all apparently feel the same way: the current behavior is wrong.\n> >>> Maybe we can switch it to start with an empty index after all?\n> >>\n> >> Starting empty may match intuition better. (More importantly, perhaps,\n> >> it's harder to come up with a use-case for --orphan which doesn't\n> >> involve starting with a clean slate.)\n> >\n> > OK so the new --orphan description would be like this, right?\n> >\n> > --8<--\n> > --orphan <new-branch>::\n> >       Create a new 'orphan' branch, named `<new-branch>`. If\n> >       `<start-point>` is specified, the working tree is adjusted to\n> >       match it. The index remains empty (i.e. no file is tracked).\n> > -->8--\n>\n> What happens if no <start-point> is given? Do you end up with an empty\n> working tree or the current one? I'd lean towards an empty working tree\n> (with a check that there are no uncommitted changes, users can use\n> `restore` if they want some of the files back) but that is inconsistent\n> with the implicit <start-point> of -c.\n\nI was thinking default <start-point> is HEAD. But yeah empty tree\nmakes more sense since you can always say \"switch --orphan <branch>\nHEAD\" but can't really say \"give me an empty tree\".\n-- \nDuy\n"},{"id":"371500","messageId":"CACsJy8BATpbAG8hDs6n2sfDCgY_XTJtU0MRxT+2FpHETqr+bCg@mail.gmail.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-14T14:13:04Z","receivedAt":"2019-03-14T14:13:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > +-f::\n> > +--force::\n> > +     Proceed even if the index or the working tree differs from\n> > +     HEAD. Both the index and working tree are restored to match\n> > +     the switching target. This is used to throw away local\n> > +     changes.\n>\n> I'd always thought that --force meant \"throw away my local changes if\n> they conflict with the new branch\" not \"throw them away regardless\"\n> (which is better as it is deterministic). Maybe we can come up with a\n> clearer name here --discard-changes? At the moment --force does not\n> throw away conflicts properly (see the script below in my comments about\n> --merge).\n\nFirst victim of --discard-changes (or maybe I misread your comment),\nit's too much to type even with completion and I'm so used to the\nshort and sweet \"switch -[d]f\".\n\nUnless people object, I'm going to keep --force as an alias for\n--discard-changes. -f could be extended later to cover more\n--ignore-stuff when it makes sense.\n-- \nDuy\n"},{"id":"371504","messageId":"CABPp-BF3_p3+fmQcWYEu2z3J4FfPmDmiMyFiBRXyz8TxKLL7jA@mail.gmail.com","threadId":"50360","inReplyTo":"20190314091753.GA9852@ash","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-14T14:46:51Z","receivedAt":"2019-03-14T14:47:05Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Mar 14, 2019 at 2:18 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Mar 12, 2019 at 01:28:35PM -0400, Eric Sunshine wrote:\n> > > > Again, not much of a datapoint, but I do use --orphan periodically.\n> > > > The idea of \"fixing\" the behavior so that --orphan starts with a clean\n> > > > slate is certainly appealing (since it matches how I've used orphan\n> > > > branches in each case).\n> > >\n> > > The only three people who have commented on --orphan in this thread\n> > > all apparently feel the same way: the current behavior is wrong.\n> > > Maybe we can switch it to start with an empty index after all?\n> >\n> > Starting empty may match intuition better. (More importantly, perhaps,\n> > it's harder to come up with a use-case for --orphan which doesn't\n> > involve starting with a clean slate.)\n>\n> OK so the new --orphan description would be like this, right?\n>\n> --8<--\n> --orphan <new-branch>::\n>         Create a new 'orphan' branch, named `<new-branch>`. If\n>         `<start-point>` is specified, the working tree is adjusted to\n>         match it. The index remains empty (i.e. no file is tracked).\n> -->8--\n>\n> I was wondering if instead of the empty index, we mark on files from\n> <start-point> as intent-to-add. That way \"git commit -a\" achieves the\n> same as before, but you could still carefully craft the new index and\n> \"git commit\". Dunno. Not going to implement it unless somebody says\n> something, since I rarely (if ever?) use --orphan.\n\nI don't see why <start-point> even makes sense to use with --orphan;\nyou should error if both are given, IMO.  The point of --orphan is to\ncreate some entirely new history.  So, I'd expect \"git switch --orphan\n<new-branch>\" to:\n  * not create refs/heads/<new-branch>\n  * set HEAD to refs/heads/<new-branch>\n  * empty all tracked files from the working tree.\n  * empty the index\n\nAlternatively, you could allow <start-point> to be passed with\n--orphan, adjusting the above steps so that both the index and the\nworking tree are switched to match <start-point>, but ONLY if\n<start-point> defaults to the empty tree when --orphan is passed.\n\n> I may need someone to come up with a convincing commit message\n> too. All I've got is \"I've been told this is a good thing to do\" :)\n\nHow about:\n\n\"\"\"\nSwitching and creating branches always involves knowing the\n<start-point> to begin the new branch from.  Sometimes, people want to\ncreate a new branch that does not have any commits yet; --orphan is a\nflag to allow that.  --orphan overrides the default of HEAD for\n<start-point> instead causing us to start from an empty history.  The\nuse of --orphan is incompatible with specifying a <start-point>.\n\"\"\"\n"},{"id":"371521","messageId":"CABPp-BHks42bhFr68TvM-Hu185EjuqoYdPexbJeWEepBaK9kyA@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqsgvp9axu.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-14T15:48:55Z","receivedAt":"2019-03-14T15:49:11Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Mar 14, 2019 at 12:23 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Elijah Newren <newren@gmail.com> writes:\n>\n> >> I think sometimes a 3-way merge creates marker conflicts in file, and\n> >> this does not look easy to reverse when switching back. If it's true\n> >> and we can detect it, we can still abort this case (i.e. requiring -m\n>\n> Paths with actual conflicts are much easier to recover from than\n> paths that cleanly merge.   You have your original in stage #2, so\n> you should be able to \"restore-path --stage=2 --from-stage path...\"\n> them.  Once the contents are auto-resolved cleanly, however, the\n> cached contents are automaticaly updated to the auto-resolved result,\n> and it needs more work to reverse the effect of the merge (it is\n> doable, of course, as you know exactly the contents of the\n> switched-to branch and the contents of the switching-from branch, so\n> it is just the matter of running 3-way merge in the right direction\n> to recover what used to be in the working tree).\n\nI agree that mixtures of conflicts and clean merges would be the most\ndifficult to reverse.  However, recovering from conflict cases is\nactually harder than you mention, at least when renames are involved.\nIf foo is modified on both sides of history in conflicting ways AND\nrenamed to bar in the other branch, then all three staged will be\nstored under the path bar.  Using your method to restore would cause\nyou to get a file named bar (though it would have the contents of the\noriginal foo).\n\nIt can unfortunately get even worse.  With rename/rename(2to1)\nconflicts; there are up to six values that needed to be shoved into\nthe normal three slots in the index, which merge-recursive achieves by\nfirst content merging three at a time and shoving the (possibly\nconflict-marker containing) results from that into two slots of the\nindex and then two-way merging from there to get the worktree contents\n(possibly resulting in nested conflict markers).  Checking something\nout of stage 2 thus not only might get the file path wrong but can\nalso get you contents with conflict markers.\n\n\nHowever, this whole exercise gave me an idea that answers Duy's\noriginal question definitively: you cannot necessarily reverse a\nsuccessful (i.e. no conflicts present) three-way merge with another\nthree-way merge.  Here's an example to demonstrate that...let's say\nyou are on branch A, and you have two identical files:\n  A: foo, bar\nand you want to switch to branch B which renamed foo but didn't modify it:\n  B: baz, bar\nlocally on A you had renamed bar but didn't modify it:\n  C: foo, baz\n(C isn't an actual commit; I just wanted a label to refer to it)\n\ndoing a \"git switch -m B\" will result in merge-recursive noting that\nboth foo and bar were renamed to baz but that both versions of baz\nwhere identical, so we'd end up with:\n  D: baz\n(D isn't an actual commit; I just wanted a label to refer to it)\n\nNow, if we were to run \"git switch -m A\" to go back to A,\nmerge-recursive would need to do a three-way merge of D & A using B as\nthe base.  merge-recursive would note that A renamed baz->foo, and\nthat D deleted bar.  So, you'd end up with:\n  E: foo\n\nUnfortunately, C != E, so our reversing was unsuccessful.\n\n\nIn summary, this is yet another reason that making --merge the default\nfor either checkout or switch would be unsafe.\n"},{"id":"371563","messageId":"CA+P7+xpotm+W-4RRhchWvZ_mkr=eVKC4B8fKp8_gxBNGn+wrQA@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BHpoa+tNW=7+6TOmoQTvbap3iavqApEGny4E-rwv1gZVQ@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2019-03-15T06:11:37Z","receivedAt":"2019-03-15T06:11:52Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Mar 12, 2019 at 9:52 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Tue, Mar 12, 2019 at 8:37 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> >\n> > On Tue, Mar 12, 2019 at 8:19 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > > On Tue, Mar 12, 2019 at 3:51 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > > > I tend to agree with this but that's probably because I don't really use\n> > > > checkout -B. I'm not sure if it's widely used or not. I do find checkout\n> > > > -b convenient though.\n> > >\n> > > Yeah I think both -b and -B are about convenience.\n> > >\n> > > But I would not mind dropping -C for now, if people think it's not\n> > > that useful. We can bring it back in incremental updates if we realize\n> > > we miss it so much. I'll keep it unless somebody says something.\n> >\n> > It's not much of a datapoint, but I do use \"git checkout -B\" (and\n> > therefore would use \"git switch -C\") periodically (in addition to\n> > -b/-c, which I use all the time). And, convenience is important,\n> > especially considering that \"git switch\" is already more painful in\n> > some ways than \"git checkout\", due to having to trigger and spell out\n> > certain things explicitly (such as detaching).\n>\n> Ooh, interesting.  I haven't used it and didn't know who did, but\n> since you do you can probably answer the question surrounding the\n> long-name for the -C option from earlier in the thread:\n>\n> Do you use checkout -B only when checkout -b fails, or do you use it\n> pre-emptively?  The former would suggest we should use a name like\n> --recreate, while the latter would suggest a name more like\n> --force-create.\n\nI use it periodically sometimes like this:\n\ngit checkout -B master origin/master\n\nEssentially, I use it as a way to quickly and forcefully re-create a\nbranch that tracks the remote branch.\n\nFor example, I might have made a commit or two on master, and realized\nI should be doing a separate branch, and I want to quickly make that a\nbranch by doing\n\ngit checkout -b some-branch.\n\nThen, later I want to switch back to master, and I really just want\nmaster to be exactly what the remote had. Sure I can do that in a lot\nof ways, but it's nice to have a somewhat convenient shortcut to do it\nin one command.\n\nSo, for me, it's natural to think of this as a \"--force-create\", but\nuse of either --recreate or --force- create work with my setup.\n\nBasically, for *me*, I almost always use it when I know there's a\nbranch that I want to re-setup there, and rarely verify that fact\nusing a \"oh, -b failed, I guess I need -B\"\n\nRegards,\nJake\n"},{"id":"371569","messageId":"CAPig+cQPMZhd-JSxD4Z6pL19qB1bFNu+EvPDh0vADpcaJ0tbqw@mail.gmail.com","threadId":"50360","inReplyTo":"20190313183604.GA27503@esm","subject":"Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-15T08:19:07Z","receivedAt":"2019-03-15T08:19:22Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 13, 2019 at 2:36 PM Eckhard Maaß\n<eckhard.s.maass@googlemail.com> wrote:\n> On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > Similar to automatic detach, this behavior could be confusing because\n> > it can sometimes create a new branch without a user asking it to,\n> > especially when the user is still not aware about this feature.\n> >\n> > In the future, perhaps we could have a config key to disable these\n> > safety nets and let 'switch' do automatic detach or dwim\n> > again. But that will be opt-in after the user knows what is what. For\n> > now give a short option if you want to use it often.\n>\n> As I am late to the patch series (sorry!), has there been already any\n> discussion on that? In my experience, people get confused with detached\n> HEAD state quite often, whereas the automatic creation of a local branch\n> is no problem.\n\nThis statement does a good job of articulating my (unspoken) response\nto this patch. Whereas a detached HEAD might be scary and confusing to\nnewcomers, and difficult for them to recover from, automatic creation\nof a DWIM'd local branch doesn't seem so problematic (if at all).\n\nWith git-checkout, it's very easy to accidentally get into a detached\nHEAD state, so it makes some sense to protect newcomers, by default,\nfrom that accident in git-switch. However, auto-creation of a new\nlocal branch is not, for a couple reasons, nearly so weighty a matter.\nFirst, in many cases it may be less likely to happen since it requires\npresence of a corresponding remote tracking branch. Second, it's\nintuitively easy to recover from it: when git-switch reports that it\ncreated a new branch, though perhaps surprising, the user would\nnaturally know to look for a command to \"delete a branch\".\n\nAnd, unlike a detached HEAD, which newcomers may mistakenly believe\nlead to irretrievable loss of work, an unexpected branch creation\ncarries no such penalty, perceived or real.\n\n> So if it is deemed to be too confusing to dwim in this\n> case, could we add a hint suggesting the command? Something like:\n> \"No suitable branch <foo> found, however there is a remote tracking\n> branch <origin/foo> that you can siwtch and create with `git switch\n> --guess foo`\" (or maybe the one without guess)?\n\nThat could be helpful if git-switch continues to make --no-guess the\ndefault, but making --guess the default (as it is in git-checkout)\nwould likely be even better (for the reasons enumerated above).\n"},{"id":"371571","messageId":"CACsJy8CG1-6Fp72451fwVRVHuYchLqU0uda=AJbK_aXvhW4YmQ@mail.gmail.com","threadId":"50360","inReplyTo":"CAPig+cQPMZhd-JSxD4Z6pL19qB1bFNu+EvPDh0vADpcaJ0tbqw@mail.gmail.com","subject":"Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-15T09:29:18Z","receivedAt":"2019-03-15T09:29:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 15, 2019 at 3:19 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Wed, Mar 13, 2019 at 2:36 PM Eckhard Maaß\n> <eckhard.s.maass@googlemail.com> wrote:\n> > On Fri, Mar 08, 2019 at 04:57:48PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > > Similar to automatic detach, this behavior could be confusing because\n> > > it can sometimes create a new branch without a user asking it to,\n> > > especially when the user is still not aware about this feature.\n> > >\n> > > In the future, perhaps we could have a config key to disable these\n> > > safety nets and let 'switch' do automatic detach or dwim\n> > > again. But that will be opt-in after the user knows what is what. For\n> > > now give a short option if you want to use it often.\n> >\n> > As I am late to the patch series (sorry!), has there been already any\n> > discussion on that? In my experience, people get confused with detached\n> > HEAD state quite often, whereas the automatic creation of a local branch\n> > is no problem.\n>\n> This statement does a good job of articulating my (unspoken) response\n> to this patch. Whereas a detached HEAD might be scary and confusing to\n> newcomers, and difficult for them to recover from, automatic creation\n> of a DWIM'd local branch doesn't seem so problematic (if at all).\n>\n> With git-checkout, it's very easy to accidentally get into a detached\n> HEAD state, so it makes some sense to protect newcomers, by default,\n> from that accident in git-switch. However, auto-creation of a new\n> local branch is not, for a couple reasons, nearly so weighty a matter.\n> First, in many cases it may be less likely to happen since it requires\n> presence of a corresponding remote tracking branch. Second, it's\n> intuitively easy to recover from it: when git-switch reports that it\n> created a new branch, though perhaps surprising, the user would\n> naturally know to look for a command to \"delete a branch\".\n>\n> And, unlike a detached HEAD, which newcomers may mistakenly believe\n> lead to irretrievable loss of work, an unexpected branch creation\n> carries no such penalty, perceived or real.\n\nI can't remember the last time it was discussed, but part of the\nreasons I chose to default --no-guess is because completion will be a\nlot less noisy.\n\nBut that's a very personal preference. I will switch to --guess as\ndefault no problem (unless someone jumps in and screams NOOO of\ncourse).\n\nPlease don't hold back when you find something not quite right. At\nleast now I can fix it. Either that or by the time it's released, the\nInternet will blame me for adding yet another confusing git command :P\n-- \nDuy\n"},{"id":"371643","messageId":"CACsJy8Ddc+pYJRqX7ELEdryomNnG48i0Oncr1L0SjV0A7b6n8A@mail.gmail.com","threadId":"50360","inReplyTo":"20190313183604.GA27503@esm","subject":"Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-16T03:59:12Z","receivedAt":"2019-03-16T03:59:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 14, 2019 at 1:36 AM Eckhard Maaß\n<eckhard.s.maass@googlemail.com> wrote:\n> And while at it - what should happen, if:\n>\n> - there is a tag named example\n> - no local branch example\n> - a branch at origin/example\n>\n> ... and we switch then? Right now it just gives \"cannot find branch\",\n> should there be more information? Should it even create a branch\n> example? With switch, switching a branch is unambiguous, even though\n> there is a tag with that name. If I really want to --guess - should I be\n> given more information?\n\nThere's checkout.defaultRemote that changes this behavior and it\naffects git-switch as well.\n\nI like the idea of giving more information than just the terse (and\ntechnically correct) \"cannot find branch\". But I may have to delay\nimplementing this a bit. There's a been a lot of updates in git-switch\n(which is great, don't get me wrong, I appreciate the feedback). I\nneed to get v4 out for review soon then maybe handle the rest in v5\n(and finally take a real hard look at git-restore comments).\n-- \nDuy\n"},{"id":"371679","messageId":"20190317060023.3651-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"[PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T06:00:23Z","receivedAt":"2019-03-17T06:00:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One-way merge is supposed to take stat info from the index and\neverything else from the given tree. This implies stage 0 because trees\ncan't have non-zero stages. The add_entry(.., old, ...) call however\nwill keep stage index from the index.\n\nThis is normally not a problem if the entry from the index is\nnormal (stage #0). But if there is a conflict, we'll get stage #1 entry\nas \"old\" and it gets recorded in the final index. Fix it by clearing\nstage mask.\n\nThis bug probably comes from b5b425074e (git-read-tree: make one-way\nmerge also honor the \"update\" flag, 2005-06-07). Before this commit, we\nmay create the final (\"dst\") index entry from the one in index, but we\ndo clear CE_STAGEMASK.\n\nI briefly checked two- and three-way merge functions. I think we don't\nhave the same problem in those.\n\nReported-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This is one of the two bugs reported by Phillip. It's not tangled with\n nd/switch-and-restore code changes and I'm sending it separately.\n\n t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++\n unpack-trees.c                     |  2 +-\n 2 files changed, 27 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t2026-checkout-force.sh b/t/t2026-checkout-force.sh\nnew file mode 100755\nindex 0000000000..272ccf533a\n--- /dev/null\n+++ b/t/t2026-checkout-force.sh\n@@ -0,0 +1,26 @@\n+#!/bin/sh\n+\n+test_description='checkout --force'\n+. ./test-lib.sh\n+\n+test_expect_success 'force checking out a conflict' '\n+\techo a >a &&\n+\tgit add a &&\n+\tgit commit -ama &&\n+\tA_OBJ=$(git rev-parse :a) &&\n+\tgit branch topic &&\n+\techo b >a &&\n+\tgit commit -amb &&\n+\tB_OBJ=$(git rev-parse :a) &&\n+\tgit checkout topic &&\n+\techo c >a &&\n+\tC_OBJ=$(git hash-object a) &&\n+\tgit checkout -m master &&\n+\ttest_cmp_rev :1:a $A_OBJ &&\n+\ttest_cmp_rev :2:a $B_OBJ &&\n+\ttest_cmp_rev :3:a $C_OBJ &&\n+\tgit checkout -f topic &&\n+\ttest_cmp_rev :a $A_OBJ\n+'\n+\n+test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 22c41a3ba8..1ccd343cad 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,\n \t\tif (o->update && S_ISGITLINK(old->ce_mode) &&\n \t\t    should_update_submodules() && !verify_uptodate(old, o))\n \t\t\tupdate |= CE_UPDATE;\n-\t\tadd_entry(o, old, update, 0);\n+\t\tadd_entry(o, old, update, CE_STAGEMASK);\n \t\treturn 0;\n \t}\n \treturn merged_entry(a, old, o);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371694","messageId":"20190317124926.17137-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190308095752.8574-1-pclouds@gmail.com","subject":"[PATCH v4 00/26] Add new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:00Z","receivedAt":"2019-03-17T12:49:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Thanks for all the comments from v3 (and before), I didn't expect\nfeedback from so many people. v4 fixes most of them, but still leaves\na couple for v5.\n\n- -C remains because people seem to need it\n\n- --recreate vs --force-create: there was no strong preference for\n  either, so I stick with --force-create\n\n- --guess (dwim mode) is now made default\n\n- --orphan by default switches to an empty tree instead of current HEAD\n  (this is Elijah's alternative approach). This allows you to start\n  fresh, but you can still keep \"HEAD\" content or even switch to another\n  tree.\n\n- advice.detachedHead text is updated to suggest a way out (currently it\n  only suggests a way forward with 'checkout -b')\n\n- --force has a new alias --discard-changes\n\n- the error message when we expect a branch is improved to help point\n  out why the given argument is not a branch\n\n- more docs update and reorganization, and other silly bugs I made in\n  v3\n\nThe two things I'm aware of but have not done:\n\n- the bug in 'git checkout -m' and 'git switch -m' that could lead to\n  loss of staged changes.\n\n- better suggestion when dwim fails (e.g. when there are multiple dwim\n  candidates, we could show them all).\n\nThere have been lots of discussions back and forth. If I miss anything\nelse, please let me know.\n\nNguyễn Thái Ngọc Duy (26):\n  git-checkout.txt: spell out --no-option\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  git-checkout.txt: fix monospace typeset\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: advice how to get out of detached HEAD mode\n  checkout: keep most #include sorted\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command 'switch'\n  switch: better names for -b and -B\n  switch: add --discard-changes\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: implicit dwim, use --no-guess to disable it\n  switch: no worktree status unless real branch switch happens\n  switch: reject if some operation is in progress\n  switch: --orphan defaults to empty tree as HEAD\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |  12 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                | 221 ++++---\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   5 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt (new)            | 277 ++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  19 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/gitworkflows.txt                |   3 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  56 +-\n Makefile                                      |   1 +\n advice.c                                      |  17 +-\n builtin.h                                     |   1 +\n builtin/checkout.c                            | 597 ++++++++++--------\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  37 +-\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   1 +\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |  28 +-\n t/t2060-switch.sh (new +x)                    |  97 +++\n 39 files changed, 1043 insertions(+), 493 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n\nRange-diff dựa trên v3:\n 1:  b0cb2372db !  1:  535dc1f310 doc: document --overwrite-ignore\n    @@ -22,7 +22,20 @@\n     +\n      --recurse-submodules::\n      --no-recurse-submodules::\n    - \tUsing --recurse-submodules will update the content of all initialized\n    +-\tUsing --recurse-submodules will update the content of all initialized\n    ++\tUsing `--recurse-submodules` will update the content of all initialized\n    + \tsubmodules according to the commit recorded in the superproject. If\n    + \tlocal modifications in a submodule would be overwritten the checkout\n    +-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n    ++\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n    + \tis used, the work trees of submodules will not be updated.\n    +-\tJust like linkgit:git-submodule[1], this will detach the\n    +-\tsubmodules HEAD.\n    ++\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    ++\tsubmodule.\n    + \n    + --no-guess::\n    + \tDo not attempt to create a branch if a remote tracking branch\n     \n      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\n      --- a/Documentation/git-merge.txt\n 2:  5b165524d1 <  -:  ---------- git-checkout.txt: fix monospace typeset\n -:  ---------- >  2:  b6305d2871 git-checkout.txt: fix monospace typeset\n 3:  a4240a888e =  3:  bb56e45457 t: rename t2014-switch.sh to t2014-checkout-switch.sh\n -:  ---------- >  4:  123392757a checkout: advice how to get out of detached HEAD mode\n -:  ---------- >  5:  d1ec6b4ce0 checkout: keep most #include sorted\n 4:  dc4a8e8933 =  6:  4b1742672b checkout: factor out some code in parse_branchname_arg()\n 5:  f0d933c2ac =  7:  e0bcc3a4dd checkout: make \"opts\" in cmd_checkout() a pointer\n 6:  0aa541689c =  8:  ca5b4d7db0 checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n 7:  84ca42bb26 =  9:  13c03997f0 checkout: split options[] array in three pieces\n 8:  e983c8bb2c ! 10:  24d532b276 checkout: split part of it to new command 'switch'\n    @@ -4,9 +4,9 @@\n     \n         \"git checkout\" doing too many things is a source of confusion for many\n         users (and it even bites old timers sometimes). To remedy that, the\n    -    command will be split into two new ones: switch and\n    -    something-to-checkout-paths. The good old \"git checkout\" command is\n    -    still here and will be until all (or most of users) are sick of it.\n    +    command will be split into two new ones: switch and restore. The good\n    +    old \"git checkout\" command is still here and will be until all (or most\n    +    of users) are sick of it.\n     \n         See the new man page for the final design of switch. The actual\n         implementation though is still pretty much the same as \"git checkout\"\n    @@ -157,12 +157,13 @@\n     +updated to match the branch. All new commits will be added to the tip\n     +of this branch.\n     +\n    -+Optionally a new branch could be created with either `-c` or `-C`, or\n    ++Optionally a new branch could be created with either `-c`, `-C`,\n    ++automatically from a remote branch of same name (see `--guess`), or\n     +detach the working tree from any branch with `--detach`, along with\n     +switching.\n     +\n     +Switching branches does not require a clean index and working tree\n    -+(i.e. no differences compared to 'HEAD'). The operation is aborted\n    ++(i.e. no differences compared to `HEAD`). The operation is aborted\n     +however if the switch leads to loss of local changes, unless told\n     +otherwise.\n     +\n    @@ -175,14 +176,19 @@\n     +\tName for the new branch.\n     +\n     +<start-point>::\n    -+\tThe name of a commit at which to switch to before creating a\n    -+\tnew branch or detach from.\n    ++\tThe starting point for the new branch. Specifying a\n    ++\t`<start-point>` allows you to create a branch based on some\n    ++\tother point in history than where HEAD currently points. (Or,\n    ++\tin the case of `--detach`, allows you to inspect and detach\n    ++\tfrom some other point.)\n     ++\n    -+You can use the `\"@{-N}\"` syntax to refer to the N-th last\n    ++You can use the `@{-N}` syntax to refer to the N-th last\n     +branch/commit switched to \"git switch\" or \"git checkout\"\n    -+operation. You may also specify `-` which is synonymous to `\"@{-1}\"`.\n    ++operation. You may also specify `-` which is synonymous to `@{-1}`.\n    ++This is often used to switch quickly between two branches, or to undo\n    ++a branch switch by mistake.\n     ++\n    -+As a special case, you may use `\"A...B\"` as a shortcut for the merge\n    ++As a special case, you may use `A...B` as a shortcut for the merge\n     +base of `A` and `B` if there is exactly one merge base. You can leave\n     +out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n     +\n    @@ -214,8 +220,8 @@\n     +\texperiments. See the \"DETACHED HEAD\" section in\n     +\tlinkgit:git-checkout[1] for details.\n     +\n    -+-g::\n     +--guess::\n    ++--no-guess::\n     +\tIf `<branch>` is not found but there does exist a tracking\n     +\tbranch in exactly one remote (call it `<remote>`) with a\n     +\tmatching name, treat as equivalent to\n    @@ -231,18 +237,45 @@\n     +to always checkout remote branches from there if `<branch>` is\n     +ambiguous but exists on the 'origin' remote. See also\n     +`checkout.defaultRemote` in linkgit:git-config[1].\n    -+\n    -+-q::\n    -+--quiet::\n    -+\tQuiet, suppress feedback messages.\n    +++\n    ++`--guess` is the default behavior. Use `--no-guess` to disable it.\n     +\n     +-f::\n     +--force::\n    ++\tAn alias for `--discard-changes`.\n    ++\n    ++--discard-changes::\n     +\tProceed even if the index or the working tree differs from\n    -+\tHEAD. Both the index and working tree are restored to match\n    ++\t`HEAD`. Both the index and working tree are restored to match\n     +\tthe switching target. This is used to throw away local\n     +\tchanges.\n     +\n    ++-m::\n    ++--merge::\n    ++\tIf you have local modifications to one or more files that are\n    ++\tdifferent between the current branch and the branch to which\n    ++\tyou are switching, the command refuses to switch branches in\n    ++\torder to preserve your modifications in context.  However,\n    ++\twith this option, a three-way merge between the current\n    ++\tbranch, your working tree contents, and the new branch is\n    ++\tdone, and you will be on the new branch.\n    +++\n    ++When a merge conflict happens, the index entries for conflicting\n    ++paths are left unmerged, and you need to resolve the conflicts\n    ++and mark the resolved paths with `git add` (or `git rm` if the merge\n    ++should result in deletion of the path).\n    ++\n    ++--conflict=<style>::\n    ++\tThe same as `--merge` option above, but changes the way the\n    ++\tconflicting hunks are presented, overriding the\n    ++\t`merge.conflictStyle` configuration variable.  Possible values are\n    ++\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n    ++\t\"merge\" style, shows the original contents).\n    ++\n    ++-q::\n    ++--quiet::\n    ++\tQuiet, suppress feedback messages.\n    ++\n     +--progress::\n     +--no-progress::\n     +\tProgress status is reported on the standard error stream\n    @@ -253,49 +286,28 @@\n     +-t::\n     +--track::\n     +\tWhen creating a new branch, set up \"upstream\" configuration.\n    -+\t`-c` is implied. See \"--track\" in linkgit:git-branch[1] for\n    ++\t`-c` is implied. See `--track` in linkgit:git-branch[1] for\n     +\tdetails.\n     ++\n     +If no `-c` option is given, the name of the new branch will be derived\n     +from the remote-tracking branch, by looking at the local part of the\n     +refspec configured for the corresponding remote, and then stripping\n    -+the initial part up to the \"*\".  This would tell us to use \"hack\" as\n    -+the local branch when branching off of \"origin/hack\" (or\n    -+\"remotes/origin/hack\", or even \"refs/remotes/origin/hack\").  If the\n    ++the initial part up to the \"*\".  This would tell us to use `hack` as\n    ++the local branch when branching off of `origin/hack` (or\n    ++`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the\n     +given name has no slash, or the above guessing results in an empty\n     +name, the guessing is aborted.  You can explicitly give a name with\n     +`-c` in such a case.\n     +\n     +--no-track::\n     +\tDo not set up \"upstream\" configuration, even if the\n    -+\tbranch.autoSetupMerge configuration variable is true.\n    -+\n    -+-m::\n    -+--merge::\n    -+\tIf you have local modifications to one or more files that are\n    -+\tdifferent between the current branch and the branch to which\n    -+\tyou are switching, the command refuses to switch branches in\n    -+\torder to preserve your modifications in context.  However,\n    -+\twith this option, a three-way merge between the current\n    -+\tbranch, your working tree contents, and the new branch is\n    -+\tdone, and you will be on the new branch.\n    -++\n    -+When a merge conflict happens, the index entries for conflicting\n    -+paths are left unmerged, and you need to resolve the conflicts\n    -+and mark the resolved paths with `git add` (or `git rm` if the merge\n    -+should result in deletion of the path).\n    -+\n    -+--conflict=<style>::\n    -+\tThe same as --merge option above, but changes the way the\n    -+\tconflicting hunks are presented, overriding the\n    -+\tmerge.conflictStyle configuration variable.  Possible values are\n    -+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n    -+\t\"merge\" style, shows the original contents).\n    ++\t`branch.autoSetupMerge` configuration variable is true.\n     +\n     +--orphan <new-branch>::\n    -+\tCreate a new 'orphan' branch, named `<new-branch>`, started from\n    -+\t`<start-point>` and switch to it. See explanation of the same\n    -+\toption in linkgit:git-checkout[1] for details.\n    ++\tCreate a new 'orphan' branch, named `<new-branch>`. If\n    ++\t`<start-point>` is specified, the index and working tree are\n    ++\tadjusted to match it. Otherwise both are adjusted to contain no\n    ++\ttracked files.\n     +\n     +--ignore-other-worktrees::\n     +\t`git switch` refuses when the wanted ref is already\n    @@ -303,15 +315,20 @@\n     +\tthe ref out anyway. In other words, the ref can be held by\n     +\tmore than one worktree.\n     +\n    ++--ignore-in-progress::\n    ++\t`git switch` by default refuses when some operation is in\n    ++\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n    ++\toverrides this safety check and allows switching.\n    ++\n     +--recurse-submodules::\n     +--no-recurse-submodules::\n    -+\tUsing --recurse-submodules will update the content of all initialized\n    ++\tUsing `--recurse-submodules` will update the content of all initialized\n     +\tsubmodules according to the commit recorded in the superproject. If\n     +\tlocal modifications in a submodule would be overwritten the checkout\n    -+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n    ++\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n     +\tis used, the work trees of submodules will not be updated.\n    -+\tJust like linkgit:git-submodule[1], this will detach the\n    -+\tsubmodules HEAD.\n    ++\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    ++\tsubmodule.\n     +\n     +EXAMPLES\n     +--------\n    @@ -366,7 +383,7 @@\n     +------------\n     +\n     +If you want to start a new branch from a remote branch of the same\n    -+name, use `-g`:\n    ++name:\n     +\n     +------------\n     +$ git switch -g new-topic\n    @@ -374,7 +391,7 @@\n     +Switched to a new branch 'new-topic'\n     +------------\n     +\n    -+To check out commit \"HEAD~3\" for temporary inspection or experiment\n    ++To check out commit `HEAD~3` for temporary inspection or experiment\n     +without creating a new branch:\n     +\n     +------------\n    @@ -391,7 +408,8 @@\n     +\n     +SEE ALSO\n     +--------\n    -+linkgit:git-checkout[1]\n    ++linkgit:git-checkout[1],\n    ++linkgit:git-branch[1]\n     +\n     +GIT\n     +---\n 9:  5491932cc8 = 11:  c966bacfcc switch: better names for -b and -B\n -:  ---------- > 12:  bdb88bf9a9 switch: add --discard-changes\n10:  e0f1247b09 = 13:  d5fe7f4bd0 switch: remove -l\n11:  f7ff1a7df6 ! 14:  3bce4c521e switch: stop accepting pathspec\n    @@ -11,9 +11,9 @@\n      --- a/builtin/checkout.c\n      +++ b/builtin/checkout.c\n     @@\n    - \tint count_checkout_paths;\n      \tint overlay_mode;\n      \tint no_dwim_new_local_branch;\n    + \tint discard_changes;\n     +\tint accept_pathspec;\n      \n      \t/*\n12:  0750d63d38 ! 15:  dad0063fc4 switch: reject \"do nothing\" case\n    @@ -17,8 +17,8 @@\n      --- a/builtin/checkout.c\n      +++ b/builtin/checkout.c\n     @@\n    - \tint overlay_mode;\n      \tint no_dwim_new_local_branch;\n    + \tint discard_changes;\n      \tint accept_pathspec;\n     +\tint switch_branch_doing_nothing_is_ok;\n      \n13:  bcd843146d ! 16:  41ca042917 switch: only allow explicit detached HEAD\n    @@ -24,6 +24,36 @@\n      \tint writeout_stage;\n      \tint overwrite_ignore;\n      \tint ignore_skipworktree;\n    +@@\n    + \treturn status;\n    + }\n    + \n    ++static void die_expecting_a_branch(const struct branch_info *branch_info)\n    ++{\n    ++\tstruct object_id oid;\n    ++\tchar *to_free;\n    ++\n    ++\tif (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {\n    ++\t\tconst char *ref = to_free;\n    ++\n    ++\t\tif (skip_prefix(ref, \"refs/tags/\", &ref))\n    ++\t\t\tdie(_(\"a branch is expected, got tag '%s'\"), ref);\n    ++\t\tif (skip_prefix(ref, \"refs/remotes/\", &ref))\n    ++\t\t\tdie(_(\"a branch is expected, got remote branch '%s'\"), ref);\n    ++\t\tdie(_(\"a branch is expected, got '%s'\"), ref);\n    ++\t}\n    ++\tif (branch_info->commit)\n    ++\t\tdie(_(\"a branch is expected, got commit '%s'\"), branch_info->name);\n    ++\t/*\n    ++\t * This case should never happen because we already die() on\n    ++\t * non-commit, but just in case.\n    ++\t */\n    ++\tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n    ++}\n    ++\n    + static int checkout_branch(struct checkout_opts *opts,\n    + \t\t\t   struct branch_info *new_branch_info)\n    + {\n     @@\n      \t    !opts->force_detach)\n      \t\tdie(_(\"missing branch or commit argument\"));\n    @@ -34,7 +64,7 @@\n     +\t    !opts->new_branch_force &&\n     +\t    new_branch_info->name &&\n     +\t    !new_branch_info->path)\n    -+\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n    ++\t\tdie_expecting_a_branch(new_branch_info);\n     +\n      \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n      \t    !opts->ignore_other_worktrees) {\n14:  d5cc55525d = 17:  a0b9f1b285 switch: add short option for --detach\n15:  4a27400a58 ! 18:  3d254df104 switch: no implicit dwim, use --guess to dwim\n    @@ -1,15 +1,15 @@\n     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n     \n    -    switch: no implicit dwim, use --guess to dwim\n    +    switch: implicit dwim, use --no-guess to disable it\n     \n    -    Similar to automatic detach, this behavior could be confusing because\n    -    it can sometimes create a new branch without a user asking it to,\n    -    especially when the user is still not aware about this feature.\n    +    This is already the default in git-checkout. The real change in here is\n    +    just minor cleanup. The main excuse is to explain why dwim is kept default.\n     \n    -    In the future, perhaps we could have a config key to disable these\n    -    safety nets and let 'switch' do automatic detach or dwim\n    -    again. But that will be opt-in after the user knows what is what. For\n    -    now give a short option if you want to use it often.\n    +    Contrary to detach mode that is easy to get into and confusing to get\n    +    back out. Automatically creating a tracking branch often does not kick\n    +    in as often (you would need a branch of the same name on a remote). And\n    +    since the branch creation is reported clearly, the user should be able\n    +    to undo/delete it if it's unwanted.\n     \n      diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n      --- a/Documentation/git-checkout.txt\n    @@ -41,7 +41,7 @@\n      rather expensive side-effects to show only the tracking information,\n     @@\n      \tDo not set up \"upstream\" configuration, even if the\n    - \tbranch.autoSetupMerge configuration variable is true.\n    + \t`branch.autoSetupMerge` configuration variable is true.\n      \n     +--guess::\n     +--no-guess::\n    @@ -68,8 +68,8 @@\n      \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n      \tdetails.\n     @@\n    - \tJust like linkgit:git-submodule[1], this will detach the\n    - \tsubmodules HEAD.\n    + \tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    + \tsubmodule.\n      \n     ---no-guess::\n     -\tDo not attempt to create a branch if a remote tracking branch\n    @@ -88,9 +88,9 @@\n      \tint overlay_mode;\n     -\tint no_dwim_new_local_branch;\n     +\tint dwim_new_local_branch;\n    + \tint discard_changes;\n      \tint accept_pathspec;\n      \tint switch_branch_doing_nothing_is_ok;\n    - \n     @@\n      \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n      \t\t\t   N_(\"update ignored files (default)\"),\n    @@ -145,15 +145,17 @@\n      \t\t\t   N_(\"create and switch to a new branch\")),\n      \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n      \t\t\t   N_(\"create/reset and switch to a branch\")),\n    -+\t\tOPT_BOOL('g', \"guess\", &opts.dwim_new_local_branch,\n    ++\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n     +\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n    + \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n    + \t\t\t N_(\"throw away local modifications\")),\n      \t\tOPT_END()\n    - \t};\n    +@@\n      \tint ret;\n      \n      \tmemset(&opts, 0, sizeof(opts));\n     -\topts.no_dwim_new_local_branch = 0;\n    -+\topts.dwim_new_local_branch = 0;\n    ++\topts.dwim_new_local_branch = 1;\n      \topts.accept_pathspec = 0;\n      \topts.switch_branch_doing_nothing_is_ok = 0;\n      \topts.implicit_detach = 0;\n16:  f7dd33abe6 ! 19:  c6ea203f36 switch: no worktree status unless real branch switch happens\n    @@ -36,8 +36,8 @@\n      --- a/builtin/checkout.c\n      +++ b/builtin/checkout.c\n     @@\n    - #include \"submodule.h\"\n    - #include \"advice.h\"\n    + #include \"unpack-trees.h\"\n    + #include \"xdiff-interface.h\"\n      \n     -static int checkout_optimize_new_branch;\n     -\n    @@ -45,7 +45,7 @@\n      \tN_(\"git checkout [<options>] <branch>\"),\n      \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n     @@\n    - \tint dwim_new_local_branch;\n    + \tint discard_changes;\n      \tint accept_pathspec;\n      \tint switch_branch_doing_nothing_is_ok;\n     -\n    @@ -220,7 +220,7 @@\n      \topts.implicit_detach = 1;\n      \n     @@\n    - \topts.dwim_new_local_branch = 0;\n    + \topts.dwim_new_local_branch = 1;\n      \topts.accept_pathspec = 0;\n      \topts.switch_branch_doing_nothing_is_ok = 0;\n     +\topts.only_merge_on_switching_branches = 1;\n -:  ---------- > 20:  5c4effc7fd switch: reject if some operation is in progress\n -:  ---------- > 21:  a4afe6a999 switch: --orphan defaults to empty tree as HEAD\n17:  5b182abea8 ! 22:  6cca78f835 t: add tests for switch\n    @@ -31,12 +31,9 @@\n     +\ttest_path_is_missing second.t\n     +'\n     +\n    -+test_expect_success 'switch to a commit' '\n    -+\ttest_must_fail git switch master^{commit}\n    -+'\n    -+\n     +test_expect_success 'switch and detach' '\n     +\ttest_when_finished git switch master &&\n    ++\ttest_must_fail git switch master^{commit} &&\n     +\tgit switch --detach master^{commit} &&\n     +\ttest_must_fail git symbolic-ref HEAD\n     +'\n    @@ -60,6 +57,7 @@\n     +test_expect_success 'force create branch from HEAD' '\n     +\ttest_when_finished git switch master &&\n     +\tgit switch --detach master &&\n    ++\ttest_must_fail git switch -c temp &&\n     +\tgit switch -C temp &&\n     +\ttest_cmp_rev master refs/heads/temp &&\n     +\techo refs/heads/temp >expected-branch &&\n    @@ -67,12 +65,15 @@\n     +\ttest_cmp expected-branch actual-branch\n     +'\n     +\n    -+test_expect_success 'new orphan branch' '\n    ++test_expect_success 'new orphan branch from empty' '\n     +\ttest_when_finished git switch master &&\n    -+\tgit switch --orphan new-orphan master^ &&\n    ++\tgit switch --orphan new-orphan &&\n     +\ttest_commit orphan &&\n     +\tgit cat-file commit refs/heads/new-orphan >commit &&\n    -+\t! grep ^parent commit\n    ++\t! grep ^parent commit &&\n    ++\tgit ls-files >tracked-files &&\n    ++\techo orphan.t >expected &&\n    ++\ttest_cmp expected tracked-files\n     +'\n     +\n     +test_expect_success 'switching ignores file of same branch name' '\n    @@ -81,16 +82,25 @@\n     +\tgit switch first-branch &&\n     +\techo refs/heads/first-branch >expected &&\n     +\tgit symbolic-ref HEAD >actual &&\n    -+\ttest_commit expected actual\n    ++\ttest_cmp expected actual\n     +'\n     +\n     +test_expect_success 'guess and create branch ' '\n     +\ttest_when_finished git switch master &&\n    -+\ttest_must_fail git switch foo &&\n    -+\tgit switch --guess foo &&\n    ++\ttest_must_fail git switch --no-guess foo &&\n    ++\tgit switch foo &&\n     +\techo refs/heads/foo >expected &&\n     +\tgit symbolic-ref HEAD >actual &&\n     +\ttest_cmp expected actual\n     +'\n     +\n    ++test_expect_success 'switching when something is in progress' '\n    ++\ttest_when_finished rm -f .git/MERGE_HEAD &&\n    ++\t# fake a merge-in-progress\n    ++\tcp .git/HEAD .git/MERGE_HEAD &&\n    ++\ttest_must_fail git switch -d @^ &&\n    ++\tgit switch --ignore-in-progress -d @^ &&\n    ++\tgit switch -\n    ++'\n    ++\n     +test_done\n18:  1575064fe6 ! 23:  e5e6f9d6f1 completion: support switch\n    @@ -10,6 +10,16 @@\n      diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n      --- a/contrib/completion/git-completion.bash\n      +++ b/contrib/completion/git-completion.bash\n    +@@\n    + #   GIT_COMPLETION_CHECKOUT_NO_GUESS\n    + #\n    + #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n    +-#     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n    ++#     and git-switch completion (e.g., completing \"foo\" when \"origin/foo\"\n    ++#     exists).\n    + \n    + case \"$COMP_WORDBREAKS\" in\n    + *:*) : great ;;\n     @@\n      \t__git_complete_index_file \"$complete_opt\"\n      }\n    @@ -24,15 +34,22 @@\n     +\t\t__gitcomp_builtin switch\n     +\t\t;;\n     +\t*)\n    -+\t\t# check if ---guess was specified to enable DWIM mode\n    -+\t\tlocal track_opt= only_local_ref=n\n    -+\t\tif [ -n \"$(__git_find_on_cmdline \"-g --guess\")\" ]; then\n    ++\t\t# check if --track, --no-track, or --no-guess was specified\n    ++\t\t# if so, disable DWIM mode\n    ++\t\tlocal track_opt=\"--track\" only_local_ref=n\n    ++\t\tif [ \"$GIT_COMPLETION_CHECKOUT_NO_GUESS\" = \"1\" ] ||\n    ++\t\t   [ -n \"$(__git_find_on_cmdline \"--track --no-track --no-guess\")\" ]; then\n    ++\t\t\ttrack_opt=''\n    ++\t\tfi\n    ++\t\t# explicit --guess enables DWIM mode regardless of\n    ++\t\t# $GIT_COMPLETION_CHECKOUT_NO_GUESS\n    ++\t\tif [ -n \"$(__git_find_on_cmdline \"--guess\")\" ]; then\n     +\t\t\ttrack_opt='--track'\n     +\t\tfi\n     +\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n     +\t\t\tonly_local_ref=y\n     +\t\tfi\n    -+\t\tif [ $only_local_ref = y -a -n \"$track_opt\"]; then\n    ++\t\tif [ $only_local_ref = y -a -z \"$track_opt\" ]; then\n     +\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n     +\t\telse\n     +\t\t\t__git_complete_refs $track_opt\n19:  8ed01ad0c5 ! 24:  807e8bc50b doc: promote \"git switch\"\n    @@ -647,23 +647,28 @@\n      void detach_advice(const char *new_name)\n      {\n      \tconst char *fmt =\n    --\t_(\"Note: checking out '%s'.\\n\\n\"\n    +-\t_(\"Note: checking out '%s'.\\n\"\n     +\t_(\"Note: switching to '%s'.\\n\"\n    -+\t\"\\n\"\n    + \t\"\\n\"\n      \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n      \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n    --\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n    +-\t\"state without impacting any branches by performing another checkout.\\n\"\n     +\t\"state without impacting any branches by switching back to a branch.\\n\"\n    -+\t\"\\n\"\n    + \t\"\\n\"\n      \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n    --\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n    --\t\"  git checkout -b <new-branch-name>\\n\\n\");\n    +-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n     +\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n    -+\t\"\\n\"\n    -+\t\"  git switch -c <new-branch-name>\\n\\n\");\n    + \t\"\\n\"\n    +-\t\"  git checkout -b <new-branch-name>\\n\"\n    ++\t\"  git switch -c <new-branch-name>\\n\"\n    + \t\"\\n\"\n    + \t\"Or undo this checkout with:\\n\"\n    + \t\"\\n\"\n    +-\t\"  git checkout -\\n\"\n    ++\t\"  git switch -\\n\"\n    + \t\"\\n\"\n    + \t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n      \n    - \tfprintf(stderr, fmt, new_name);\n    - }\n     \n      diff --git a/sha1-name.c b/sha1-name.c\n      --- a/sha1-name.c\n    @@ -700,8 +705,13 @@\n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n      \n    - \tHEAD is now at \\$commit three\n    - \tEOF\n    + \tOr undo this checkout with:\n    + \n    +-\t  git checkout -\n    ++\t  git switch -\n    + \n    + \tTurn off this advice by setting config variable advice.detachedHead to false\n    + \n     @@\n      \n      \t# The first detach operation is more chatty than the following ones.\n    @@ -721,5 +731,10 @@\n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n      \n    - \tHEAD is now at \\$commit... three\n    - \tEOF\n    + \tOr undo this checkout with:\n    + \n    +-\t  git checkout -\n    ++\t  git switch -\n    + \n    + \tTurn off this advice by setting config variable advice.detachedHead to false\n    + \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371695","messageId":"20190317124926.17137-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 01/26] git-checkout.txt: spell out --no-option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:01Z","receivedAt":"2019-03-17T12:49:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's easier to search for and also less cryptic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f179b43732..99c8c0dc0f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -118,7 +118,8 @@ OPTIONS\n --quiet::\n \tQuiet, suppress feedback messages.\n \n---[no-]progress::\n+--progress::\n+--no-progress::\n \tProgress status is reported on the standard error stream\n \tby default when it is attached to a terminal, unless `--quiet`\n \tis specified. This flag enables progress reporting even if not\n@@ -262,7 +263,7 @@ edits from your current working tree. See the ``Interactive Mode''\n section of linkgit:git-add[1] to learn how to operate the `--patch` mode.\n +\n Note that this option uses the no overlay mode by default (see also\n-`--[no-]overlay`), and currently doesn't support overlay mode.\n+`--overlay`), and currently doesn't support overlay mode.\n \n --ignore-other-worktrees::\n \t`git checkout` refuses when the wanted ref is already checked\n@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n---[no-]recurse-submodules::\n+--recurse-submodules::\n+--no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also\n \tDo not attempt to create a branch if a remote tracking branch\n \tof the same name exists.\n \n---[no-]overlay::\n+--overlay::\n+--no-overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371696","messageId":"20190317124926.17137-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 02/26] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:02Z","receivedAt":"2019-03-17T12:49:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 99c8c0dc0f..28817cfa41 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371697","messageId":"20190317124926.17137-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 03/26] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:03Z","receivedAt":"2019-03-17T12:49:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 14 ++++++++++----\n Documentation/git-merge.txt    |  5 +++++\n 2 files changed, 15 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 28817cfa41..82e0f81869 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -271,15 +271,21 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --recurse-submodules::\n --no-recurse-submodules::\n-\tUsing --recurse-submodules will update the content of all initialized\n+\tUsing `--recurse-submodules` will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n \tis used, the work trees of submodules will not be updated.\n-\tJust like linkgit:git-submodule[1], this will detach the\n-\tsubmodules HEAD.\n+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodule.\n \n --no-guess::\n \tDo not attempt to create a branch if a remote tracking branch\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..6a9163d8fe 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,11 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371698","messageId":"20190317124926.17137-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 04/26] git-checkout.txt: fix monospace typeset","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:04Z","receivedAt":"2019-03-17T12:50:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Add backticks where we have none, replace single quotes with backticks\nand replace double-quotes. Drop double-quotes from nested constructions\nsuch as `\"@{-1}\"`. Add a missing possessive apostrophe after the word\n\"submodules\" while at it.\n\nHelped-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 154 ++++++++++++++++-----------------\n 1 file changed, 77 insertions(+), 77 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 82e0f81869..bf90966c95 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current\n branch.\n \n 'git checkout' [<branch>]::\n-\tTo prepare for working on <branch>, switch to it by updating\n+\tTo prepare for working on `<branch>`, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n-\tHEAD at the branch. Local modifications to the files in the\n+\t`HEAD` at the branch. Local modifications to the files in the\n \tworking tree are kept, so that they can be committed to the\n-\t<branch>.\n+\t`<branch>`.\n +\n-If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n+If `<branch>` is not found but there does exist a tracking branch in\n+exactly one remote (call it `<remote>`) with a matching name, treat as\n equivalent to\n +\n ------------\n@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the\n 'origin' remote. See also `checkout.defaultRemote` in\n linkgit:git-config[1].\n +\n-You could omit <branch>, in which case the command degenerates to\n+You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n if exists, for the current branch.\n@@ -61,7 +61,7 @@ if exists, for the current branch.\n \t`--track` without `-b` implies branch creation; see the\n \tdescription of `--track` below.\n +\n-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it\n+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it\n is reset. This is the transactional equivalent of\n +\n ------------\n@@ -75,25 +75,25 @@ successful.\n 'git checkout' --detach [<branch>]::\n 'git checkout' [--detach] <commit>::\n \n-\tPrepare to work on top of <commit>, by detaching HEAD at it\n+\tPrepare to work on top of `<commit>`, by detaching `HEAD` at it\n \t(see \"DETACHED HEAD\" section), and updating the index and the\n \tfiles in the working tree.  Local modifications to the files\n \tin the working tree are kept, so that the resulting working\n \ttree will be the state recorded in the commit plus the local\n \tmodifications.\n +\n-When the <commit> argument is a branch name, the `--detach` option can\n-be used to detach HEAD at the tip of the branch (`git checkout\n-<branch>` would check out that branch without detaching HEAD).\n+When the `<commit>` argument is a branch name, the `--detach` option can\n+be used to detach `HEAD` at the tip of the branch (`git checkout\n+<branch>` would check out that branch without detaching `HEAD`).\n +\n-Omitting <branch> detaches HEAD at the tip of the current branch.\n+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.\n \n 'git checkout' [<tree-ish>] [--] <pathspec>...::\n \n \tOverwrite paths in the working tree by replacing with the\n-\tcontents in the index or in the <tree-ish> (most often a\n-\tcommit).  When a <tree-ish> is given, the paths that\n-\tmatch the <pathspec> are updated both in the index and in\n+\tcontents in the index or in the `<tree-ish>` (most often a\n+\tcommit).  When a `<tree-ish>` is given, the paths that\n+\tmatch the `<pathspec>` are updated both in the index and in\n \tthe working tree.\n +\n The index may contain unmerged entries because of a previous failed merge.\n@@ -128,7 +128,7 @@ OPTIONS\n -f::\n --force::\n \tWhen switching branches, proceed even if the index or the\n-\tworking tree differs from HEAD.  This is used to throw away\n+\tworking tree differs from `HEAD`.  This is used to throw away\n \tlocal changes.\n +\n When checking out paths from the index, do not fail upon unmerged\n@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. \"one contributor's work on top\n of it\").\n \n -b <new_branch>::\n-\tCreate a new branch named <new_branch> and start it at\n-\t<start_point>; see linkgit:git-branch[1] for details.\n+\tCreate a new branch named `<new_branch>` and start it at\n+\t`<start_point>`; see linkgit:git-branch[1] for details.\n \n -B <new_branch>::\n-\tCreates the branch <new_branch> and start it at <start_point>;\n-\tif it already exists, then reset it to <start_point>. This is\n+\tCreates the branch `<new_branch>` and start it at `<start_point>`;\n+\tif it already exists, then reset it to `<start_point>`. This is\n \tequivalent to running \"git branch\" with \"-f\"; see\n \tlinkgit:git-branch[1] for details.\n \n@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be\n derived from the remote-tracking branch, by looking at the local part of\n the refspec configured for the corresponding remote, and then stripping\n the initial part up to the \"*\".\n-This would tell us to use \"hack\" as the local branch when branching\n-off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n-\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n+This would tell us to use `hack` as the local branch when branching\n+off of `origin/hack` (or `remotes/origin/hack`, or even\n+`refs/remotes/origin/hack`).  If the given name has no slash, or the above\n guessing results in an empty name, the guessing is aborted.  You can\n explicitly give a name with `-b` in such a case.\n \n --no-track::\n \tDo not set up \"upstream\" configuration, even if the\n-\tbranch.autoSetupMerge configuration variable is true.\n+\t`branch.autoSetupMerge` configuration variable is true.\n \n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.\n --detach::\n \tRather than checking out a branch to work on it, check out a\n \tcommit for inspection and discardable experiments.\n-\tThis is the default behavior of \"git checkout <commit>\" when\n-\t<commit> is not a branch name.  See the \"DETACHED HEAD\" section\n+\tThis is the default behavior of `git checkout <commit>` when\n+\t`<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n \tbelow for details.\n \n --orphan <new_branch>::\n-\tCreate a new 'orphan' branch, named <new_branch>, started from\n-\t<start_point> and switch to it.  The first commit made on this\n+\tCreate a new 'orphan' branch, named `<new_branch>`, started from\n+\t`<start_point>` and switch to it.  The first commit made on this\n \tnew branch will have no parents and it will be the root of a new\n \thistory totally disconnected from all the other branches and\n \tcommits.\n +\n The index and the working tree are adjusted as if you had previously run\n-\"git checkout <start_point>\".  This allows you to start a new history\n-that records a set of paths similar to <start_point> by easily running\n-\"git commit -a\" to make the root commit.\n+`git checkout <start_point>`.  This allows you to start a new history\n+that records a set of paths similar to `<start_point>` by easily running\n+`git commit -a` to make the root commit.\n +\n This can be useful when you want to publish the tree from a commit\n without exposing its full history. You might want to do this to publish\n@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of\n code.\n +\n If you want to start a disconnected history that records a set of paths\n-that is totally different from the one of <start_point>, then you should\n+that is totally different from the one of `<start_point>`, then you should\n clear the index and the working tree right after creating the orphan\n-branch by running \"git rm -rf .\" from the top level of the working tree.\n+branch by running `git rm -rf .` from the top level of the working tree.\n Afterwards you will be ready to prepare your new files, repopulating the\n working tree, by copying them from elsewhere, extracting a tarball, etc.\n \n --ignore-skip-worktree-bits::\n \tIn sparse checkout mode, `git checkout -- <paths>` would\n-\tupdate only entries matched by <paths> and sparse patterns\n-\tin $GIT_DIR/info/sparse-checkout. This option ignores\n-\tthe sparse patterns and adds back any files in <paths>.\n+\tupdate only entries matched by `<paths>` and sparse patterns\n+\tin `$GIT_DIR/info/sparse-checkout`. This option ignores\n+\tthe sparse patterns and adds back any files in `<paths>`.\n \n -m::\n --merge::\n@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n \n --conflict=<style>::\n-\tThe same as --merge option above, but changes the way the\n+\tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n-\tmerge.conflictStyle configuration variable.  Possible values are\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n \t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n \t\"merge\" style, shows the original contents).\n \n -p::\n --patch::\n \tInteractively select hunks in the difference between the\n-\t<tree-ish> (or the index, if unspecified) and the working\n+\t`<tree-ish>` (or the index, if unspecified) and the working\n \ttree.  The chosen hunks are then applied in reverse to the\n-\tworking tree (and if a <tree-ish> was specified, the index).\n+\tworking tree (and if a `<tree-ish>` was specified, the index).\n +\n This means that you can use `git checkout -p` to selectively discard\n edits from your current working tree. See the ``Interactive Mode''\n@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-\tworking tree, but not in <tree-ish> are removed, to make them\n-\tmatch <tree-ish> exactly.\n+\tworking tree, but not in `<tree-ish>` are removed, to make them\n+\tmatch `<tree-ish>` exactly.\n \n <branch>::\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n \twhen prepended with \"refs/heads/\", is a valid ref), then that\n \tbranch is checked out. Otherwise, if it refers to a valid\n-\tcommit, your HEAD becomes \"detached\" and you are no longer on\n+\tcommit, your `HEAD` becomes \"detached\" and you are no longer on\n \tany branch (see below for details).\n +\n-You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+You can use the `@{-N}` syntax to refer to the N-th last\n branch/commit checked out using \"git checkout\" operation. You may\n-also specify `-` which is synonymous to `\"@{-1}\"`.\n+also specify `-` which is synonymous to `@{-1}`.\n +\n-As a special case, you may use `\"A...B\"` as a shortcut for the\n+As a special case, you may use `A...B` as a shortcut for the\n merge base of `A` and `B` if there is exactly one merge base. You can\n leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n <start_point>::\n \tThe name of a commit at which to start the new branch; see\n-\tlinkgit:git-branch[1] for details. Defaults to HEAD.\n+\tlinkgit:git-branch[1] for details. Defaults to `HEAD`.\n \n <tree-ish>::\n \tTree to checkout from (when paths are given). If not specified,\n@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n DETACHED HEAD\n -------------\n-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each\n branch refers to a specific commit. Let's look at a repo with three\n-commits, one of them tagged, and with branch 'master' checked out:\n+commits, one of them tagged, and with branch `master` checked out:\n \n ------------\n            HEAD (refers to branch 'master')\n@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')\n ------------\n \n When a commit is created in this state, the branch is updated to refer to\n-the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n-parent is commit 'c', and then updates branch 'master' to refer to new\n-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n-to commit 'd':\n+the new commit. Specifically, 'git commit' creates a new commit `d`, whose\n+parent is commit `c`, and then updates branch `master` to refer to new\n+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers\n+to commit `d`:\n \n ------------\n $ edit; git add; git commit\n@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n It is sometimes useful to be able to checkout a commit that is not at\n the tip of any named branch, or even to create a new commit that is not\n referenced by a named branch. Let's look at what happens when we\n-checkout commit 'b' (here we show two ways this may be done):\n+checkout commit `b` (here we show two ways this may be done):\n \n ------------\n $ git checkout v2.0  # or\n@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-Notice that regardless of which checkout command we use, HEAD now refers\n-directly to commit 'b'. This is known as being in detached HEAD state.\n-It means simply that HEAD refers to a specific commit, as opposed to\n+Notice that regardless of which checkout command we use, `HEAD` now refers\n+directly to commit `b`. This is known as being in detached `HEAD` state.\n+It means simply that `HEAD` refers to a specific commit, as opposed to\n referring to a named branch. Let's see what happens when we create a commit:\n \n ------------\n@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-There is now a new commit 'e', but it is referenced only by HEAD. We can\n+There is now a new commit `e`, but it is referenced only by `HEAD`. We can\n of course add yet another commit in this state:\n \n ------------\n@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n In fact, we can perform all the normal Git operations. But, let's look\n-at what happens when we then checkout master:\n+at what happens when we then checkout `master`:\n \n ------------\n $ git checkout master\n@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n It is important to realize that at this point nothing refers to commit\n-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted\n by the routine Git garbage collection process, unless we create a reference\n-before that happens. If we have not yet moved away from commit 'f',\n+before that happens. If we have not yet moved away from commit `f`,\n any of these will create a reference to it:\n \n ------------\n@@ -442,19 +442,19 @@ $ git branch foo        <2>\n $ git tag foo           <3>\n ------------\n \n-<1> creates a new branch 'foo', which refers to commit 'f', and then\n-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n-    be in detached HEAD state after this command.\n+<1> creates a new branch `foo`, which refers to commit `f`, and then\n+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer\n+    be in detached `HEAD` state after this command.\n \n-<2> similarly creates a new branch 'foo', which refers to commit 'f',\n-    but leaves HEAD detached.\n+<2> similarly creates a new branch `foo`, which refers to commit `f`,\n+    but leaves `HEAD` detached.\n \n-<3> creates a new tag 'foo', which refers to commit 'f',\n-    leaving HEAD detached.\n+<3> creates a new tag `foo`, which refers to commit `f`,\n+    leaving `HEAD` detached.\n \n-If we have moved away from commit 'f', then we must first recover its object\n+If we have moved away from commit `f`, then we must first recover its object\n name (typically by using git reflog), and then we can create a reference to\n-it. For example, to see the last two commits to which HEAD referred, we\n+it. For example, to see the last two commits to which `HEAD` referred, we\n can use either of these commands:\n \n ------------\n@@ -465,12 +465,12 @@ $ git log -g -2 HEAD\n ARGUMENT DISAMBIGUATION\n -----------------------\n \n-When there is only one argument given and it is not `--` (e.g. \"git\n-checkout abc\"), and when the argument is both a valid `<tree-ish>`\n-(e.g. a branch \"abc\" exists) and a valid `<pathspec>` (e.g. a file\n+When there is only one argument given and it is not `--` (e.g. `git\n+checkout abc`), and when the argument is both a valid `<tree-ish>`\n+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file\n or a directory whose name is \"abc\" exists), Git would usually ask\n you to disambiguate.  Because checking out a branch is so common an\n-operation, however, \"git checkout abc\" takes \"abc\" as a `<tree-ish>`\n+operation, however, `git checkout abc` takes \"abc\" as a `<tree-ish>`\n in such a situation.  Use `git checkout -- <pathspec>` if you want\n to checkout these paths out of the index.\n \n@@ -478,7 +478,7 @@ EXAMPLES\n --------\n \n . The following sequence checks out the `master` branch, reverts\n-  the `Makefile` to two revisions back, deletes hello.c by\n+  the `Makefile` to two revisions back, deletes `hello.c` by\n   mistake, and gets it back from the index.\n +\n ------------\n@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>\n +\n <1> switch branch\n <2> take a file out of another commit\n-<3> restore hello.c from the index\n+<3> restore `hello.c` from the index\n +\n If you want to check out _all_ C source files out of the index,\n you can say\n@@ -519,7 +519,7 @@ $ git checkout -- hello.c\n $ git checkout mytopic\n ------------\n +\n-However, your \"wrong\" branch and correct \"mytopic\" branch may\n+However, your \"wrong\" branch and correct `mytopic` branch may\n differ in files that you have modified locally, in which case\n the above checkout would fail like this:\n +\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371699","messageId":"20190317124926.17137-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:05Z","receivedAt":"2019-03-17T12:50:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371700","messageId":"20190317124926.17137-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 06/26] checkout: advice how to get out of detached HEAD mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:06Z","receivedAt":"2019-03-17T12:50:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Detached HEAD mode is considered dangerous and confusing for newcomers\nand we print a big block of warning how to move forward. But we should\nalso suggest the user the way to get out of it if they get into detached\nHEAD by mistake.\n\nWhile at there, I also suggest how to turn the advice off. This is\nanother thing I find annoying with advices and should be dealt with in a\nmore generic way. But that may require some refactoring in advice.c\nfirst.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c                   | 17 +++++++++++++----\n t/t2020-checkout-detach.sh | 12 ++++++++++++\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..b224825637 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: checking out '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"\\n\"\n+\t\"Or undo this checkout with:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -\\n\"\n+\t\"\\n\"\n+\t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..743c7248a2 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit three\n \tEOF\n \n@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit... three\n \tEOF\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371701","messageId":"20190317124926.17137-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 07/26] checkout: keep most #include sorted","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:07Z","receivedAt":"2019-03-17T12:50:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The include list becomes very long and frankly a bit unorganized. With\nthe exception of builtin.h, cache.h or git-compat-util.h which have to\ncome first, keep the rest sorted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e6037b296..f7801db2fb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1,30 +1,30 @@\n #define USE_THE_INDEX_COMPATIBILITY_MACROS\n #include \"builtin.h\"\n-#include \"config.h\"\n+#include \"advice.h\"\n+#include \"blob.h\"\n+#include \"branch.h\"\n+#include \"cache-tree.h\"\n #include \"checkout.h\"\n+#include \"commit.h\"\n+#include \"config.h\"\n+#include \"diff.h\"\n+#include \"dir.h\"\n+#include \"ll-merge.h\"\n #include \"lockfile.h\"\n+#include \"merge-recursive.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n-#include \"object-store.h\"\n-#include \"commit.h\"\n+#include \"remote.h\"\n+#include \"resolve-undo.h\"\n+#include \"revision.h\"\n+#include \"run-command.h\"\n+#include \"submodule.h\"\n+#include \"submodule-config.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n-#include \"dir.h\"\n-#include \"run-command.h\"\n-#include \"merge-recursive.h\"\n-#include \"branch.h\"\n-#include \"diff.h\"\n-#include \"revision.h\"\n-#include \"remote.h\"\n-#include \"blob.h\"\n #include \"xdiff-interface.h\"\n-#include \"ll-merge.h\"\n-#include \"resolve-undo.h\"\n-#include \"submodule-config.h\"\n-#include \"submodule.h\"\n-#include \"advice.h\"\n \n static int checkout_optimize_new_branch;\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371702","messageId":"20190317124926.17137-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 08/26] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:08Z","receivedAt":"2019-03-17T12:50:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f7801db2fb..1ce0446bc6 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371703","messageId":"20190317124926.17137-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 09/26] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:09Z","receivedAt":"2019-03-17T12:50:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1ce0446bc6..963f10b05b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371704","messageId":"20190317124926.17137-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:10Z","receivedAt":"2019-03-17T12:50:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 963f10b05b..b6fc1fc69f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -47,6 +47,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -58,6 +60,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371705","messageId":"20190317124926.17137-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 11/26] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:11Z","receivedAt":"2019-03-17T12:50:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  1 +\n 3 files changed, 77 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b6fc1fc69f..cf1d70511e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 2733393546..caaeed896f 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..9a90c332a5 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n+struct option *parse_options_dup(const struct option *a);\n struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371706","messageId":"20190317124926.17137-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 12/26] checkout: split part of it to new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:12Z","receivedAt":"2019-03-17T12:50:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and restore. The good\nold \"git checkout\" command is still here and will be until all (or most\nof users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                         |   1 +\n Documentation/config/advice.txt    |  13 +-\n Documentation/config/branch.txt    |   4 +-\n Documentation/config/checkout.txt  |   9 +-\n Documentation/config/diff.txt      |   3 +-\n Documentation/git-checkout.txt     |   4 +\n Documentation/git-switch.txt (new) | 277 +++++++++++++++++++++++++++++\n Documentation/gitattributes.txt    |   3 +-\n Documentation/githooks.txt         |   8 +-\n Makefile                           |   1 +\n builtin.h                          |   1 +\n builtin/checkout.c                 |  60 +++++--\n command-list.txt                   |   1 +\n git.c                              |   1 +\n 14 files changed, 359 insertions(+), 27 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 7374587f9d..c687b92b1c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex c4118fa196..d6872ffa83 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex bf90966c95..c7192bdefe 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -560,6 +560,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..74415f12ab\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,277 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--guess] <branch>\n+'git switch' [<options>] --detach [<start-point>]\n+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch. The working tree and the index are\n+updated to match the branch. All new commits will be added to the tip\n+of this branch.\n+\n+Optionally a new branch could be created with either `-c`, `-C`,\n+automatically from a remote branch of same name (see `--guess`), or\n+detach the working tree from any branch with `--detach`, along with\n+switching.\n+\n+Switching branches does not require a clean index and working tree\n+(i.e. no differences compared to `HEAD`). The operation is aborted\n+however if the switch leads to loss of local changes, unless told\n+otherwise.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new-branch>::\n+\tName for the new branch.\n+\n+<start-point>::\n+\tThe starting point for the new branch. Specifying a\n+\t`<start-point>` allows you to create a branch based on some\n+\tother point in history than where HEAD currently points. (Or,\n+\tin the case of `--detach`, allows you to inspect and detach\n+\tfrom some other point.)\n++\n+You can use the `@{-N}` syntax to refer to the N-th last\n+branch/commit switched to \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `@{-1}`.\n+This is often used to switch quickly between two branches, or to undo\n+a branch switch by mistake.\n++\n+As a special case, you may use `A...B` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new-branch>::\n+--create <new-branch>::\n+\tCreate a new branch named `<new-branch>` starting at\n+\t`<start-point>` before switching to the branch. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-C <new-branch>::\n+--force-create <new-branch>::\n+\tSimilar to `--create` except that if `<new-branch>` already\n+\texists, it will be reset to `<start-point>`. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch -f <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n++\n+`--guess` is the default behavior. Use `--no-guess` to disable it.\n+\n+-f::\n+--force::\n+\tAn alias for `--discard-changes`.\n+\n+--discard-changes::\n+\tProceed even if the index or the working tree differs from\n+\t`HEAD`. Both the index and working tree are restored to match\n+\tthe switching target. This is used to throw away local\n+\tchanges.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See `--track` in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use `hack` as\n+the local branch when branching off of `origin/hack` (or\n+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\t`branch.autoSetupMerge` configuration variable is true.\n+\n+--orphan <new-branch>::\n+\tCreate a new 'orphan' branch, named `<new-branch>`. If\n+\t`<start-point>` is specified, the index and working tree are\n+\tadjusted to match it. Otherwise both are adjusted to contain no\n+\ttracked files.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--ignore-in-progress::\n+\t`git switch` by default refuses when some operation is in\n+\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n+\toverrides this safety check and allows switching.\n+\n+--recurse-submodules::\n+--no-recurse-submodules::\n+\tUsing `--recurse-submodules` will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodule.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name:\n+\n+------------\n+$ git switch -g new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit `HEAD~3` for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1],\n+linkgit:git-branch[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 9b41f81c06..cd0f9fa507 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 5bf653c111..8ff72f0613 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -406,7 +407,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 148668368b..8e91db73ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cf1d70511e..dd3c200cb5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 2014aab6b8..39582cf511 100644\n--- a/git.c\n+++ b/git.c\n@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371707","messageId":"20190317124926.17137-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 13/26] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:13Z","receivedAt":"2019-03-17T12:51:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout. Move -b/-B\nto cmd_checkout() and new -c/-C with the same functionality in\ncmd_switch_branch()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex dd3c200cb5..400a0d5620 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371708","messageId":"20190317124926.17137-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 14/26] switch: add --discard-changes","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:14Z","receivedAt":"2019-03-17T12:51:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"--discard-changes is a better name than --force for this option since\nit's what really happens. --force is turned to an alias for\n--discard-changes. But it's meant to an alias for potentially more force\noptions in the future.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 12 ++++++++++--\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 400a0d5620..bd94b04303 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint discard_changes;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n-\tif (opts->force) {\n+\tif (opts->discard_changes) {\n \t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n \t\t\t\t opts, 1, writeout_error);\n \t\tif (ret)\n@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->force && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \tif (opts->force && opts->merge)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-f\", \"-m\");\n \n+\tif (opts->discard_changes && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--discard-changes\", \"--merge\");\n+\n \tif (opts->force_detach && opts->new_branch)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"),\n \t\t    \"--detach\", \"-b/-B/--orphan\");\n@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n+\tif (opts->force)\n+\t\topts->discard_changes = 1;\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n+\t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371709","messageId":"20190317124926.17137-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 15/26] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:15Z","receivedAt":"2019-03-17T12:51:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex bd94b04303..85fc60f5f2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371710","messageId":"20190317124926.17137-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 16/26] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:16Z","receivedAt":"2019-03-17T12:51:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 85fc60f5f2..2c4cab6406 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -54,6 +54,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371711","messageId":"20190317124926.17137-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 17/26] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:17Z","receivedAt":"2019-03-17T12:51:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2c4cab6406..73b7373414 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -55,6 +55,7 @@ struct checkout_opts {\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"missing branch or commit argument\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371712","messageId":"20190317124926.17137-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 18/26] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:18Z","receivedAt":"2019-03-17T12:51:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++\n 1 file changed, 34 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 73b7373414..d72e28aabb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -45,6 +45,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)\n \treturn status;\n }\n \n+static void die_expecting_a_branch(const struct branch_info *branch_info)\n+{\n+\tstruct object_id oid;\n+\tchar *to_free;\n+\n+\tif (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {\n+\t\tconst char *ref = to_free;\n+\n+\t\tif (skip_prefix(ref, \"refs/tags/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got tag '%s'\"), ref);\n+\t\tif (skip_prefix(ref, \"refs/remotes/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got remote branch '%s'\"), ref);\n+\t\tdie(_(\"a branch is expected, got '%s'\"), ref);\n+\t}\n+\tif (branch_info->commit)\n+\t\tdie(_(\"a branch is expected, got commit '%s'\"), branch_info->name);\n+\t/*\n+\t * This case should never happen because we already die() on\n+\t * non-commit, but just in case.\n+\t */\n+\tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n+}\n+\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"missing branch or commit argument\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie_expecting_a_branch(new_branch_info);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371713","messageId":"20190317124926.17137-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 19/26] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:19Z","receivedAt":"2019-03-17T12:51:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d72e28aabb..a14133b474 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371714","messageId":"20190317124926.17137-21-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 20/26] switch: implicit dwim, use --no-guess to disable it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:20Z","receivedAt":"2019-03-17T12:51:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is already the default in git-checkout. The real change in here is\njust minor cleanup. The main excuse is to explain why dwim is kept default.\n\nContrary to detach mode that is easy to get into and confusing to get\nback out. Automatically creating a tracking branch often does not kick\nin as often (you would need a branch of the same name on a remote). And\nsince the branch creation is reported clearly, the user should be able\nto undo/delete it if it's unwanted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++-------\n 2 files changed, 31 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex c7192bdefe..58f18a0842 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t`<branch>`.\n +\n If `<branch>` is not found but there does exist a tracking branch in\n-exactly one remote (call it `<remote>`) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it `<remote>`) with a matching name and\n+`--no-guess` is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \t`branch.autoSetupMerge` configuration variable is true.\n \n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n \tsubmodule.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --overlay::\n --no-overlay::\n \tIn the default overlay mode, `git checkout` never\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a14133b474..ee74379038 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,7 +53,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n \t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371715","messageId":"20190317124926.17137-22-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 21/26] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:21Z","receivedAt":"2019-03-17T12:51:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 134 ++----------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 8 insertions(+), 148 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex d6872ffa83..6b646813ab 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n-\n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ee74379038..2aeaac092d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -26,8 +26,6 @@\n #include \"unpack-trees.h\"\n #include \"xdiff-interface.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -57,11 +55,7 @@ struct checkout_opts {\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\t /*\n-\t  * Do the merge if this is the initial checkout. We cannot use\n-\t  * is_cache_unborn() here because the index hasn't been loaded yet\n-\t  * so cache_nr and timestamp.sec are always zero.\n-\t  */\n-\tif (!file_exists(get_index_file()))\n-\t\treturn 0;\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n \n \ttrace2_cmd_mode(\"branch\");\n \n@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,\n \n static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"diff.ignoresubmodules\")) {\n \t\tstruct checkout_opts *opts = cb;\n \t\thandle_ignore_submodules_arg(&opts->diff_options, value);\n@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371716","messageId":"20190317124926.17137-23-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 22/26] switch: reject if some operation is in progress","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:22Z","receivedAt":"2019-03-17T12:51:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Unless you know what you're doing, switching to another branch to do\nsomething then switching back could be confusing. Worse, you may even\nforget that you're in the middle of something. By the time you realize,\nyou may have done a ton of work and it gets harder to go back.\n\nThe new option --ignore-in-progress is added to override this check.\nHowever it's probably just safer to create a separate worktree for\nthat separate thing you want to work on and leave this worktree\nalone (unless of course creating or preparing worktrees are not cheap).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2aeaac092d..21a585eaa9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -24,6 +24,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\n+#include \"wt-status.h\"\n #include \"xdiff-interface.h\"\n \n static const char * const checkout_usage[] = {\n@@ -56,6 +57,7 @@ struct checkout_opts {\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n+\tint can_switch_when_in_progress;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !new_branch_info->path)\n \t\tdie_expecting_a_branch(new_branch_info);\n \n+\tif (!opts->can_switch_when_in_progress) {\n+\t\tstruct wt_status_state state;\n+\n+\t\tmemset(&state, 0, sizeof(state));\n+\t\twt_status_get_state(the_repository, &state, 0);\n+\n+\t\tif (state.merge_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while merging\"));\n+\t\tif (state.am_in_progress)\n+\t\t\tdie(_(\"cannot switch branch in the middle of an am session\"));\n+\t\tif (state.rebase_in_progress || state.rebase_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while rebasing\"));\n+\t\tif (state.cherry_pick_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while cherry-picking\"));\n+\t\tif (state.revert_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while reverting\"));\n+\t\tif (state.bisect_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while bisecting\"));\n+\t}\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1514,6 +1536,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n+\topts.can_switch_when_in_progress = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1539,6 +1562,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n \t\t\t N_(\"throw away local modifications\")),\n+\t\tOPT_BOOL(0, \"ignore-in-progress\", &opts.can_switch_when_in_progress,\n+\t\t\t N_(\"allow to switch branch when some operation is still in progress\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1549,6 +1574,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n+\topts.can_switch_when_in_progress = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371717","messageId":"20190317124926.17137-24-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 23/26] switch: --orphan defaults to empty tree as HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:23Z","receivedAt":"2019-03-17T12:52:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Switching and creating branches always involves knowing the\n<start-point> to begin the new branch from. Sometimes, people want to\ncreate a new branch that does not have any commits yet; --orphan is a\nflag to allow that.\n\nIf <start-point> is not specified, instead of leaving index/worktree\nunchanged, default to \"empty tree\" starting point, allowing a clean\nstart.\n\nA note on the implementation. An alternative is just create a dummy\ncommit in-core with empty tree and switch to it. But there's a chance\nthe commit's SHA-1 may end up somewhere permanent like reflog. It's best\nto make sure \"commit\" pointer is NULL to avoid it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 33 +++++++++++++++++++++++++--------\n 1 file changed, 25 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 21a585eaa9..f0d6207236 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -58,6 +58,7 @@ struct checkout_opts {\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n \tint can_switch_when_in_progress;\n+\tint orphan_default_empty_tree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -568,15 +569,19 @@ static int merge_working_tree(const struct checkout_opts *opts,\n {\n \tint ret;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tstruct tree *new_tree;\n \n \thold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(NULL) < 0)\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n+\tif (opts->orphan_default_empty_tree && !new_branch_info->commit)\n+\t\tnew_tree = parse_tree_indirect(the_hash_algo->empty_tree);\n+\telse\n+\t\tnew_tree = get_commit_tree(new_branch_info->commit);\n \tif (opts->discard_changes) {\n-\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n-\t\t\t\t opts, 1, writeout_error);\n+\t\tret = reset_tree(new_tree, opts, 1, writeout_error);\n \t\tif (ret)\n \t\t\treturn ret;\n \t} else {\n@@ -614,7 +619,8 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\t\t   &old_branch_info->commit->object.oid :\n \t\t\t\t\t   the_hash_algo->empty_tree);\n \t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n-\t\ttree = parse_tree_indirect(&new_branch_info->commit->object.oid);\n+\t\tparse_tree(new_tree);\n+\t\ttree = new_tree;\n \t\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n \n \t\tret = unpack_trees(2, trees, &topts);\n@@ -663,7 +669,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\n \n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 1,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\n@@ -672,13 +678,13 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.branch1 = new_branch_info->name;\n \t\t\to.branch2 = \"local\";\n \t\t\tret = merge_trees(&o,\n-\t\t\t\t\t  get_commit_tree(new_branch_info->commit),\n+\t\t\t\t\t  new_tree,\n \t\t\t\t\t  work,\n \t\t\t\t\t  get_commit_tree(old_branch_info->commit),\n \t\t\t\t\t  &result);\n \t\t\tif (ret < 0)\n \t\t\t\texit(128);\n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tstrbuf_release(&o.obuf);\n@@ -696,7 +702,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->discard_changes && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet && new_branch_info->commit)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -897,7 +903,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \tadd_pending_object(&revs, object, oid_to_hex(&object->oid));\n \n \tfor_each_ref(add_pending_uninteresting_ref, &revs);\n-\tadd_pending_oid(&revs, \"HEAD\", &new_commit->object.oid, UNINTERESTING);\n+\tif (new_commit)\n+\t\tadd_pending_oid(&revs, \"HEAD\",\n+\t\t\t\t&new_commit->object.oid,\n+\t\t\t\tUNINTERESTING);\n \n \tif (prepare_revision_walk(&revs))\n \t\tdie(_(\"internal error in revision walk\"));\n@@ -941,6 +950,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \n \t\tif (opts->only_merge_on_switching_branches)\n \t\t\tdo_merge = 0;\n+\n+\t\tif (opts->new_orphan_branch && opts->orphan_default_empty_tree) {\n+\t\t\tnew_branch_info->commit = NULL;\n+\t\t\tnew_branch_info->name = \"(empty)\";\n+\t\t\tdo_merge = 1;\n+\t\t}\n \t}\n \n \tif (do_merge) {\n@@ -1537,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n+\topts.orphan_default_empty_tree = 0;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1575,6 +1591,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \topts.can_switch_when_in_progress = 0;\n+\topts.orphan_default_empty_tree = 1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371718","messageId":"20190317124926.17137-25-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 24/26] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:24Z","receivedAt":"2019-03-17T12:52:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t2060-switch.sh (new +x) | 97 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 97 insertions(+)\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..62ec1bb924\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,97 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch master^{commit} &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\ttest_cmp_rev master^ refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\ttest_must_fail git switch -c temp &&\n+\tgit switch -C temp &&\n+\ttest_cmp_rev master refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch from empty' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --orphan new-orphan &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit &&\n+\tgit ls-files >tracked-files &&\n+\techo orphan.t >expected &&\n+\ttest_cmp expected tracked-files\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch --no-guess foo &&\n+\tgit switch foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'switching when something is in progress' '\n+\ttest_when_finished rm -f .git/MERGE_HEAD &&\n+\t# fake a merge-in-progress\n+\tcp .git/HEAD .git/MERGE_HEAD &&\n+\ttest_must_fail git switch -d @^ &&\n+\tgit switch --ignore-in-progress -d @^ &&\n+\tgit switch -\n+'\n+\n+test_done\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371719","messageId":"20190317124926.17137-26-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 25/26] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:25Z","receivedAt":"2019-03-17T12:52:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-\n 1 file changed, 36 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 976e4a6548..b24bc48276 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -37,7 +37,8 @@\n #   GIT_COMPLETION_CHECKOUT_NO_GUESS\n #\n #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n-#     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n+#     and git-switch completion (e.g., completing \"foo\" when \"origin/foo\"\n+#     exists).\n \n case \"$COMP_WORDBREAKS\" in\n *:*) : great ;;\n@@ -2158,6 +2159,40 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if --track, --no-track, or --no-guess was specified\n+\t\t# if so, disable DWIM mode\n+\t\tlocal track_opt=\"--track\" only_local_ref=n\n+\t\tif [ \"$GIT_COMPLETION_CHECKOUT_NO_GUESS\" = \"1\" ] ||\n+\t\t   [ -n \"$(__git_find_on_cmdline \"--track --no-track --no-guess\")\" ]; then\n+\t\t\ttrack_opt=''\n+\t\tfi\n+\t\t# explicit --guess enables DWIM mode regardless of\n+\t\t# $GIT_COMPLETION_CHECKOUT_NO_GUESS\n+\t\tif [ -n \"$(__git_find_on_cmdline \"--guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\tfi\n+\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y -a -z \"$track_opt\" ]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371720","messageId":"20190317124926.17137-27-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v4 26/26] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:49:26Z","receivedAt":"2019-03-17T12:52:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt           | 12 +++---\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 ++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 19 +++++----\n Documentation/giteveryday.txt          | 24 +++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/gitworkflows.txt         |  3 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 56 +++++++++++++-------------\n advice.c                               | 10 ++---\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             | 20 ++++-----\n 18 files changed, 101 insertions(+), 101 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0cd87ddeff..1e2d89b174 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -297,7 +297,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>\n NOTES\n -----\n \n-If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n-a branch and check it out with a single command.\n+If you are creating a branch that you want to switch to immediately,\n+it is easier to use the \"git switch\" command with its `-c` option to\n+do the same thing with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n serve four related but different purposes:\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..0a24a5679e 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 5629ba4c5d..cb6fc166e2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 132f8e55f6..cbf901efb4 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..f880d21dfb 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n-$ git reset --hard master^2\n-$ git checkout master\n+$ git switch -C mybranch master^2\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..ad455f3e39 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch -C pu next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt\nindex ca11c7bdaf..abc0dc6bc7 100644\n--- a/Documentation/gitworkflows.txt\n+++ b/Documentation/gitworkflows.txt\n@@ -301,8 +301,7 @@ topics on 'next':\n .Rewind and rebuild next\n [caption=\"Recipe: \"]\n =====================================\n-* `git checkout next`\n-* `git reset --hard master`\n+* `git switch -C next master`\n * `git merge ai/topic_in_next1`\n * `git merge ai/topic_in_next2`\n * ...\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..94799faa2b 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n-accept an arbitrary commit; for example, you can check out the commit\n-referenced by a tag:\n+The `git switch` command normally expects a branch head, but will also\n+accept an arbitrary commit when invoked with --detach; for example,\n+you can check out the commit referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex b224825637..27e39e6514 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\"\n+\t_(\"Note: switching to '%s'.\\n\"\n \t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"state without impacting any branches by switching back to a branch.\\n\"\n \t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\"\n \t\"\\n\"\n \t\"Or undo this checkout with:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -\\n\"\n+\t\"  git switch -\\n\"\n \t\"\\n\"\n \t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..da0518c8e3 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 743c7248a2..da6809b48f 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tOr undo this checkout with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \n@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tOr undo this checkout with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371721","messageId":"CACsJy8BevFGXt-RL_1G263ntXYee69N055TY8_GQL1unvJNPnw@mail.gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"Re: [PATCH v4 00/26] Add new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-17T12:58:02Z","receivedAt":"2019-03-17T12:58:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Mar 17, 2019 at 7:49 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Thanks for all the comments from v3 (and before), I didn't expect\n> feedback from so many people. v4 fixes most of them, but still leaves\n> a couple for v5.\n>\n> - -C remains because people seem to need it\n>\n> - --recreate vs --force-create: there was no strong preference for\n>   either, so I stick with --force-create\n>\n> - --guess (dwim mode) is now made default\n>\n> - --orphan by default switches to an empty tree instead of current HEAD\n>   (this is Elijah's alternative approach). This allows you to start\n>   fresh, but you can still keep \"HEAD\" content or even switch to another\n>   tree.\n>\n> - advice.detachedHead text is updated to suggest a way out (currently it\n>   only suggests a way forward with 'checkout -b')\n>\n> - --force has a new alias --discard-changes\n>\n> - the error message when we expect a branch is improved to help point\n>   out why the given argument is not a branch\n>\n> - more docs update and reorganization, and other silly bugs I made in\n>   v3\n\nI did forget one thing:\n\n- 'git switch' refuses to switch in the middle of\nrebase/bisect/cherry-pick/... session. Add --ignore-in-progress to\noverride this\n\nNow that I mention this, --ignore-in-progress should probably be part\nof --force too. Hmm...\n-- \nDuy\n"},{"id":"371746","messageId":"CAN0heSrWzU-2hgoU6oG12CwtQFH83Cgc8Eh7gXbXL3ZSQE_4vg@mail.gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-4-pclouds@gmail.com","subject":"Re: [PATCH v4 03/26] doc: document --overwrite-ignore","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-03-17T19:18:35Z","receivedAt":"2019-03-17T19:18:49Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Sun, 17 Mar 2019 at 13:49, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> I added this option in git-checkout and git-merge in c1d7036b6b\n> (checkout,merge: disallow overwriting ignored files with\n> --no-overwrite-ignore - 2011-11-27) but did not remember to update\n> documentation. This completes that commit.\n\n> +--overwrite-ignore::\n> +--no-overwrite-ignore::\n> +       Silently overwrite ignored files when switching branches. This\n> +       is the default behavior. Use `--no-overwrite-ignore` to abort\n> +       the operation when the new branch contains ignored files.\n> +\n>  --recurse-submodules::\n>  --no-recurse-submodules::\n> -       Using --recurse-submodules will update the content of all initialized\n> +       Using `--recurse-submodules` will update the content of all initialized\n>         submodules according to the commit recorded in the superproject. If\n>         local modifications in a submodule would be overwritten the checkout\n> -       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n> +       will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n>         is used, the work trees of submodules will not be updated.\n> -       Just like linkgit:git-submodule[1], this will detach the\n> -       submodules HEAD.\n> +       Just like linkgit:git-submodule[1], this will detach `HEAD` of the\n> +       submodule.\n\nAll of this `--recurse-submodules` stuff seems to have have ended\nup in the wrong commit.\n\n\nMartin\n"},{"id":"371747","messageId":"CAN0heSockAdYiNeHtx+AJVH2cDKj3Q_BfmrtVtiBMxhGbaxUWA@mail.gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-5-pclouds@gmail.com","subject":"Re: [PATCH v4 04/26] git-checkout.txt: fix monospace typeset","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-03-17T19:21:05Z","receivedAt":"2019-03-17T19:21:19Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Sun, 17 Mar 2019 at 13:50, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Add backticks where we have none, replace single quotes with backticks\n> and replace double-quotes. Drop double-quotes from nested constructions\n> such as `\"@{-1}\"`. Add a missing possessive apostrophe after the word\n> \"submodules\" while at it.\n\nAssuming you move those \"submodules\" hunks from an earlier commit, this\nlast part needs some tweaking since you're not adding a \"'\", but you\nactually rephrase the docs to avoid a buggy AsciiDoc rendering. (And if\na while-at-it needs too much explaining, maybe just skip it altogether?\nI dunno.)\n\nOther than some things appearing in the wrong commit(?), this matches\nthe \"how about this?\" I sent for v3 and our discussion of that.\n\n\n\nMartin\n"},{"id":"371761","messageId":"xmqqr2b56ir6.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CABPp-BF3_p3+fmQcWYEu2z3J4FfPmDmiMyFiBRXyz8TxKLL7jA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T02:03:57Z","receivedAt":"2019-03-18T02:04:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> I don't see why <start-point> even makes sense to use with --orphan;\n> you should error if both are given, IMO.  The point of --orphan is to\n> create some entirely new history.  So, I'd expect \"git switch --orphan\n> <new-branch>\" to:\n>   * not create refs/heads/<new-branch>\n>   * set HEAD to refs/heads/<new-branch>\n>   * empty all tracked files from the working tree.\n>   * empty the index\n>\n> Alternatively, you could allow <start-point> to be passed with\n> --orphan, adjusting the above steps so that both the index and the\n> working tree are switched to match <start-point>, but ONLY if\n> <start-point> defaults to the empty tree when --orphan is passed.\n\nDo you mean that it's like <start-point> is not really a start-point\nbut is an initial tree, i.e.\n\n\tswitch --orphan --initial-tree=<tree-ish> <new-branch>\n\nis a mere short-hand for\n\n\tswitch --orhpan <new-branch> &&\n\trestore --from-tree=<tree-ish> .\n\nI think that does make sense, but at the same time, I think a major\nreason why people say \"checkout does too many things depending on\nthe arguments and conext to be easily explained\" is exactly due to\nits many \"if you give X, it is like writing this longer command\nsequence\" short hands, so...\n\n> How about:\n>\n> \"\"\"\n> Switching and creating branches always involves knowing the\n> <start-point> to begin the new branch from.  Sometimes, people want to\n> create a new branch that does not have any commits yet; --orphan is a\n> flag to allow that.  --orphan overrides the default of HEAD for\n> <start-point> instead causing us to start from an empty history.  The\n> use of --orphan is incompatible with specifying a <start-point>.\n> \"\"\"\n\nWith or without the short-hand to say which initial tree to populate\nthe index from, the above description makes sense to me.\n\n"},{"id":"371768","messageId":"xmqqh8c06dgq.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190317060023.3651-1-pclouds@gmail.com","subject":"Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T03:58:13Z","receivedAt":"2019-03-18T04:00:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> One-way merge is supposed to take stat info from the index and\n> everything else from the given tree. This implies stage 0 because trees\n> can't have non-zero stages. The add_entry(.., old, ...) call however\n> will keep stage index from the index.\n>\n> This is normally not a problem if the entry from the index is\n> normal (stage #0). But if there is a conflict, we'll get stage #1 entry\n> as \"old\" and it gets recorded in the final index. Fix it by clearing\n> stage mask.\n>\n> This bug probably comes from b5b425074e (git-read-tree: make one-way\n> merge also honor the \"update\" flag, 2005-06-07). Before this commit, we\n> may create the final (\"dst\") index entry from the one in index, but we\n> do clear CE_STAGEMASK.\n\nWow, good find.  That's an old one.\n\n> I briefly checked two- and three-way merge functions. I think we don't\n> have the same problem in those.\n>\n> Reported-by: Phillip Wood <phillip.wood123@gmail.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  This is one of the two bugs reported by Phillip. It's not tangled with\n>  nd/switch-and-restore code changes and I'm sending it separately.\n\nThanks.\n\n>\n>  t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++\n\nThis makes it cumbersome to have the same fix in the maintenance track\nas t2026 is already in use over there.  Do we need an entirely new test\njust to house this new single test?\n\nBy the way, I am beginning to like these \"in-line\" summaries (as\nopposed to the --summary at the end), although I admit that it has\nbeen quite a while since its introduction.  Good job, again.\n\n>  unpack-trees.c                     |  2 +-\n>  2 files changed, 27 insertions(+), 1 deletion(-)\n>\n> diff --git a/t/t2026-checkout-force.sh b/t/t2026-checkout-force.sh\n> new file mode 100755\n> index 0000000000..272ccf533a\n> --- /dev/null\n> +++ b/t/t2026-checkout-force.sh\n> @@ -0,0 +1,26 @@\n> +#!/bin/sh\n> +\n> +test_description='checkout --force'\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'force checking out a conflict' '\n> +\techo a >a &&\n> +\tgit add a &&\n> +\tgit commit -ama &&\n> +\tA_OBJ=$(git rev-parse :a) &&\n> +\tgit branch topic &&\n> +\techo b >a &&\n> +\tgit commit -amb &&\n> +\tB_OBJ=$(git rev-parse :a) &&\n> +\tgit checkout topic &&\n> +\techo c >a &&\n> +\tC_OBJ=$(git hash-object a) &&\n> +\tgit checkout -m master &&\n> +\ttest_cmp_rev :1:a $A_OBJ &&\n> +\ttest_cmp_rev :2:a $B_OBJ &&\n> +\ttest_cmp_rev :3:a $C_OBJ &&\n> +\tgit checkout -f topic &&\n> +\ttest_cmp_rev :a $A_OBJ\n\nSo in short, \"checkout -f\" should have given us an entry for path\n\"a\", taken from the tip of the 'topic' branch, at stage #0 while\nswitching to that branch, but it didn't?  That would be a nice\nsummary to have at the beginning of the log message before going\ninto the implementation detail of how that happens.\n\n> +'\n> +\n> +test_done\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 22c41a3ba8..1ccd343cad 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,\n>  \t\tif (o->update && S_ISGITLINK(old->ce_mode) &&\n>  \t\t    should_update_submodules() && !verify_uptodate(old, o))\n>  \t\t\tupdate |= CE_UPDATE;\n> -\t\tadd_entry(o, old, update, 0);\n> +\t\tadd_entry(o, old, update, CE_STAGEMASK);\n\nAnd the fix is obvious, makes sense and is in line with the\nobservation you made in the proposed log message.  \n\nNicely done.\n\nThanks.\n\n>  \t\treturn 0;\n>  \t}\n>  \treturn merged_entry(a, old, o);\n"},{"id":"371769","messageId":"xmqqd0mo6cv2.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CAPig+cQPMZhd-JSxD4Z6pL19qB1bFNu+EvPDh0vADpcaJ0tbqw@mail.gmail.com","subject":"Re: [PATCH v3 17/21] switch: no implicit dwim, use --guess to dwim","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T04:11:13Z","receivedAt":"2019-03-18T04:13:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> With git-checkout, it's very easy to accidentally get into a detached\n> HEAD state, so it makes some sense to protect newcomers, by default,\n> from that accident in git-switch. However, auto-creation of a new\n> local branch is not, for a couple reasons, nearly so weighty a matter.\n> First, in many cases it may be less likely to happen since it requires\n> presence of a corresponding remote tracking branch. Second, it's\n> intuitively easy to recover from it: when git-switch reports that it\n> created a new branch, though perhaps surprising, the user would\n> naturally know to look for a command to \"delete a branch\".\n>\n> And, unlike a detached HEAD, which newcomers may mistakenly believe\n> lead to irretrievable loss of work, an unexpected branch creation\n> carries no such penalty, perceived or real.\n\nI suspect that it actually cuts both ways.  An auto-vivified local\nbranch 'foo' that happens to have the same name as 'origin/foo' is\nstill a different line of history UNLESS the user makes sure the\ndivergence is reconciled with \"push\" and \"pull\" (possibly configured\nto \"pull --rebase\", but that is a secondary detail that depends on\nthe workflow).  Even when 'switch' reports that it created a new\nbranch, a newcomer would probably not realize that it is \"new\", and\ndifferent from the branch that the remote has.\n\n>> So if it is deemed to be too confusing to dwim in this\n>> case, could we add a hint suggesting the command? Something like:\n>> \"No suitable branch <foo> found, however there is a remote tracking\n>> branch <origin/foo> that you can siwtch and create with `git switch\n>> --guess foo`\" (or maybe the one without guess)?\n>\n> That could be helpful if git-switch continues to make --no-guess the\n> default, but making --guess the default (as it is in git-checkout)\n> would likely be even better (for the reasons enumerated above).\n\nI do not have a huge issue with making \"--guess\" the default, but\nthen the user may be entitled to help with an advice message when\nthe command, especially with the implicit \"--guess\", auto-vivifies a\nnew local branch based on a remote-tracking branch to stress that\nthis is a new branch and different from the history the remote side\nhas.\n"},{"id":"371771","messageId":"xmqq4l806avp.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8BevFGXt-RL_1G263ntXYee69N055TY8_GQL1unvJNPnw@mail.gmail.com","subject":"Re: [PATCH v4 00/26] Add new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T04:54:02Z","receivedAt":"2019-03-18T04:54:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I did forget one thing:\n>\n> - 'git switch' refuses to switch in the middle of\n> rebase/bisect/cherry-pick/... session. Add --ignore-in-progress to\n> override this\n\nI think that is a sensible thing to do.  Once I did not realize that\na \"checkout\" from a failed cherry-pick with the state files left in\nthe .git/ directory took me to a strange state, causing some later\noperation to act on these state files as if a cherry-pick were in\nprogress (and somehow the shell prompt support did not report it,\nperhaps because the copy I use was out of sync with the binary).\n\nMaking sure that during a stateful operation the working tree won't\nget switched to a different branch is probably a good idea.\n\nI often find \"git reset --hard <some other starting point>\" after a\nfailed \"git checkout -b new <starting point> && git am -s3c\", so I'd\nbe opposed to a change to refuse a \"reset --hard\" during the middle\nof such a stateful operation, but I do not think of a reason why I\nwould want to \"switch\" in the middle.\n\n> Now that I mention this, --ignore-in-progress should probably be part\n> of --force too. Hmm...\n\nProbably.  You may want to decline other parts of what --force\nallows but when you say --force, --ignore-in-progress should be\nimplied.\n\n"},{"id":"371797","messageId":"CACsJy8CdS1oHt__SzgOxnoD=0h=iN51SWm3YjkOwYGkbzb4o9A@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqh8c06dgq.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-18T09:24:40Z","receivedAt":"2019-03-18T09:25:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 18, 2019 at 10:58 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > One-way merge is supposed to take stat info from the index and\n> > everything else from the given tree. This implies stage 0 because trees\n> > can't have non-zero stages. The add_entry(.., old, ...) call however\n> > will keep stage index from the index.\n> >\n> > This is normally not a problem if the entry from the index is\n> > normal (stage #0). But if there is a conflict, we'll get stage #1 entry\n> > as \"old\" and it gets recorded in the final index. Fix it by clearing\n> > stage mask.\n> >\n> > This bug probably comes from b5b425074e (git-read-tree: make one-way\n> > merge also honor the \"update\" flag, 2005-06-07). Before this commit, we\n> > may create the final (\"dst\") index entry from the one in index, but we\n> > do clear CE_STAGEMASK.\n>\n> Wow, good find.  That's an old one.\n\nCredit goes to Phillip for such attention to detail. If I tested this,\nI would have stopped after seeing conflict stages collapsed into one\nand missed the stage index. In fact I was swearing \"what the hell did\nhe complain about\" when looking at his test script's result, until I\nrealized stage #1 was indeed wrong.\n\n> >\n> >  t/t2026-checkout-force.sh (new +x) | 26 ++++++++++++++++++++++++++\n>\n> This makes it cumbersome to have the same fix in the maintenance track\n> as t2026 is already in use over there.  Do we need an entirely new test\n> just to house this new single test?\n\nI could not find any right file to put it in. I guess I could stick it\nin t2023-checkout-m.sh\n\n> > +test_expect_success 'force checking out a conflict' '\n> > +     echo a >a &&\n> > +     git add a &&\n> > +     git commit -ama &&\n> > +     A_OBJ=$(git rev-parse :a) &&\n> > +     git branch topic &&\n> > +     echo b >a &&\n> > +     git commit -amb &&\n> > +     B_OBJ=$(git rev-parse :a) &&\n> > +     git checkout topic &&\n> > +     echo c >a &&\n> > +     C_OBJ=$(git hash-object a) &&\n> > +     git checkout -m master &&\n> > +     test_cmp_rev :1:a $A_OBJ &&\n> > +     test_cmp_rev :2:a $B_OBJ &&\n> > +     test_cmp_rev :3:a $C_OBJ &&\n> > +     git checkout -f topic &&\n> > +     test_cmp_rev :a $A_OBJ\n>\n> So in short, \"checkout -f\" should have given us an entry for path\n> \"a\", taken from the tip of the 'topic' branch, at stage #0 while\n> switching to that branch, but it didn't?  That would be a nice\n> summary to have at the beginning of the log message before going\n> into the implementation detail of how that happens.\n\nOK. And the last line probably should be :0:a to make it clear we're\nlooking for stage #0.\n-- \nDuy\n"},{"id":"371798","messageId":"xmqqh8c0zfj2.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8CdS1oHt__SzgOxnoD=0h=iN51SWm3YjkOwYGkbzb4o9A@mail.gmail.com","subject":"Re: [PATCH] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T09:40:49Z","receivedAt":"2019-03-18T09:41:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> > +     test_cmp_rev :1:a $A_OBJ &&\n>> > +     test_cmp_rev :2:a $B_OBJ &&\n>> > +     test_cmp_rev :3:a $C_OBJ &&\n>> > +     git checkout -f topic &&\n>> > +     test_cmp_rev :a $A_OBJ\n>>\n>> So in short, \"checkout -f\" should have given us an entry for path\n>> \"a\", taken from the tip of the 'topic' branch, at stage #0 while\n>> switching to that branch, but it didn't?  That would be a nice\n>> summary to have at the beginning of the log message before going\n>> into the implementation detail of how that happens.\n>\n> OK. And the last line probably should be :0:a to make it clear we're\n> looking for stage #0.\n\nI would say \":a\" is plenty clear that it is looking for a merged\nentry without getting replaced with an overly explicit \":0:a\", but\n\":0:a\" would not hurt, either.\n\n"},{"id":"371804","messageId":"20190318113822.6195-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317060023.3651-1-pclouds@gmail.com","subject":"[PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-18T11:38:22Z","receivedAt":"2019-03-18T11:38:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Phillip found out that 'git checkout -f <branch>' does not restore\nconflict/unmerged files correctly. All tracked files should be taken\nfrom <branch> and all non-zero stages removed. Most of this is true,\nexcept that the final file could be in stage one instead of zero.\n\n\"checkout -f\" (among other commands) does this with one-way merge, which\nis supposed to take stat info from the index and everything else from\nthe given tree. The add_entry(.., old, ...) call in oneway_merge()\nthough will keep stage index from the index.\n\nThis is normally not a problem if the entry from the index is\nnormal (stage #0). But if there is a conflict, stage #0 does not exist\nand we'll get stage #1 entry as \"old\" variable, which gets recorded in\nthe final index. Fix it by clearing stage mask.\n\nThis bug probably comes from b5b425074e (git-read-tree: make one-way\nmerge also honor the \"update\" flag, 2005-06-07). Before this commit, we\nmay create the final (\"dst\") index entry from the one in index, but we\ndo clear CE_STAGEMASK.\n\nI briefly checked two- and three-way merge functions. I think we don't\nhave the same problem in those.\n\nReported-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n v2 updates log message to describe the problem and moves the test to\n t2023-checkout.m.sh\n\n t/t2023-checkout-m.sh | 24 ++++++++++++++++++++++++\n unpack-trees.c        |  2 +-\n 2 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh\nindex 7e18985134..fca3f85824 100755\n--- a/t/t2023-checkout-m.sh\n+++ b/t/t2023-checkout-m.sh\n@@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '\n \ttest_cmp both.txt.conflicted.cleaned both.txt.cleaned\n '\n \n+test_expect_success 'force checkout a conflict file creates stage zero entry' '\n+\tgit init co-force &&\n+\t(\n+\t\tcd co-force &&\n+\t\techo a >a &&\n+\t\tgit add a &&\n+\t\tgit commit -ama &&\n+\t\tA_OBJ=$(git rev-parse :a) &&\n+\t\tgit branch topic &&\n+\t\techo b >a &&\n+\t\tgit commit -amb &&\n+\t\tB_OBJ=$(git rev-parse :a) &&\n+\t\tgit checkout topic &&\n+\t\techo c >a &&\n+\t\tC_OBJ=$(git hash-object a) &&\n+\t\tgit checkout -m master &&\n+\t\ttest_cmp_rev :1:a $A_OBJ &&\n+\t\ttest_cmp_rev :2:a $B_OBJ &&\n+\t\ttest_cmp_rev :3:a $C_OBJ &&\n+\t\tgit checkout -f topic &&\n+\t\ttest_cmp_rev :0:a $A_OBJ\n+\t)\n+'\n+\n test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 22c41a3ba8..1ccd343cad 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,\n \t\tif (o->update && S_ISGITLINK(old->ce_mode) &&\n \t\t    should_update_submodules() && !verify_uptodate(old, o))\n \t\t\tupdate |= CE_UPDATE;\n-\t\tadd_entry(o, old, update, 0);\n+\t\tadd_entry(o, old, update, CE_STAGEMASK);\n \t\treturn 0;\n \t}\n \treturn merged_entry(a, old, o);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371805","messageId":"CACsJy8Dfy4csm5xpGwz6EZXqN+uMFmf4woEPTZ2WjbeDTa-9Sw@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqr2b56ir6.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-18T12:06:25Z","receivedAt":"2019-03-18T12:06:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 18, 2019 at 9:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > I don't see why <start-point> even makes sense to use with --orphan;\n> > you should error if both are given, IMO.  The point of --orphan is to\n> > create some entirely new history.  So, I'd expect \"git switch --orphan\n> > <new-branch>\" to:\n> >   * not create refs/heads/<new-branch>\n> >   * set HEAD to refs/heads/<new-branch>\n> >   * empty all tracked files from the working tree.\n> >   * empty the index\n> >\n> > Alternatively, you could allow <start-point> to be passed with\n> > --orphan, adjusting the above steps so that both the index and the\n> > working tree are switched to match <start-point>, but ONLY if\n> > <start-point> defaults to the empty tree when --orphan is passed.\n>\n> Do you mean that it's like <start-point> is not really a start-point\n> but is an initial tree, i.e.\n>\n>         switch --orphan --initial-tree=<tree-ish> <new-branch>\n>\n> is a mere short-hand for\n>\n>         switch --orhpan <new-branch> &&\n>         restore --from-tree=<tree-ish> .\n>\n> I think that does make sense, but at the same time, I think a major\n> reason why people say \"checkout does too many things depending on\n> the arguments and conext to be easily explained\" is exactly due to\n> its many \"if you give X, it is like writing this longer command\n> sequence\" short hands, so...\n\nYeah <start-point> is optional and probably only useful for people who\ndo a lot of --orphan. I did think if there's any other command that\ncould be a better host for this orphan branch creation. No such luck.\n\nPS. There's one small difference between 'switch --orphan <new-branch>\n<tree-ish>' and your switch/restore combination: the latter refuses if\nyou have any local changes because it has to remove all tracked files\nfirst, while the former only refuses when the local changes are\noutside <tree-ish>. If path A will be tracked by <tree-ish>, then\nwhatever changes in there may stay.\n-- \nDuy\n"},{"id":"371807","messageId":"CACsJy8B+VmwygpxWQ9Ug=L8bQXDVX7h6MjJwVU1cR2sbuHo6qQ@mail.gmail.com","threadId":"50360","inReplyTo":"xmqq4l806avp.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 00/26] Add new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-18T12:13:11Z","receivedAt":"2019-03-18T12:13:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 18, 2019 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:\n> I often find \"git reset --hard <some other starting point>\" after a\n\nIt still bugs me that I need to use this to abort some in-progress\noperation. There's \"git X --abort\" but I would need to find out what X\nis first. I would like \"git abort\" or something (and \"git continue\")\nbut haven't really thought that through yet. That direction may also\nlead to \"git undo\" which is another can of worms.\n\n> failed \"git checkout -b new <starting point> && git am -s3c\", so I'd\n> be opposed to a change to refuse a \"reset --hard\" during the middle\n> of such a stateful operation, but I do not think of a reason why I\n> would want to \"switch\" in the middle.\n-- \nDuy\n"},{"id":"371817","messageId":"CABPp-BFDy4ppUqmC_J_hx4kFWaC+otBZ=mbuaJK13Wxzcz_urQ@mail.gmail.com","threadId":"50360","inReplyTo":"20190318113822.6195-1-pclouds@gmail.com","subject":"Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-18T15:40:43Z","receivedAt":"2019-03-18T15:40:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 18, 2019 at 4:38 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Phillip found out that 'git checkout -f <branch>' does not restore\n> conflict/unmerged files correctly. All tracked files should be taken\n> from <branch> and all non-zero stages removed. Most of this is true,\n> except that the final file could be in stage one instead of zero.\n>\n> \"checkout -f\" (among other commands) does this with one-way merge, which\n> is supposed to take stat info from the index and everything else from\n> the given tree. The add_entry(.., old, ...) call in oneway_merge()\n> though will keep stage index from the index.\n>\n> This is normally not a problem if the entry from the index is\n> normal (stage #0). But if there is a conflict, stage #0 does not exist\n> and we'll get stage #1 entry as \"old\" variable, which gets recorded in\n> the final index. Fix it by clearing stage mask.\n>\n> This bug probably comes from b5b425074e (git-read-tree: make one-way\n> merge also honor the \"update\" flag, 2005-06-07). Before this commit, we\n> may create the final (\"dst\") index entry from the one in index, but we\n> do clear CE_STAGEMASK.\n>\n> I briefly checked two- and three-way merge functions. I think we don't\n> have the same problem in those.\n>\n> Reported-by: Phillip Wood <phillip.wood123@gmail.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThis is awesome; thanks for tracking it down and fixing.\n"},{"id":"371826","messageId":"CABPp-BE+9cmd-gR=ZRR2v1gpjduoQ0hQCL+57nmaiG45RZTB9g@mail.gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-13-pclouds@gmail.com","subject":"Re: [PATCH v4 12/26] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-18T16:48:36Z","receivedAt":"2019-03-18T16:48:52Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Mar 17, 2019 at 5:50 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> \"git checkout\" doing too many things is a source of confusion for many\n> users (and it even bites old timers sometimes). To remedy that, the\n> command will be split into two new ones: switch and restore. The good\n> old \"git checkout\" command is still here and will be until all (or most\n> of users) are sick of it.\n>\n> See the new man page for the final design of switch. The actual\n> implementation though is still pretty much the same as \"git checkout\"\n> and not completely aligned with the man page. Following patches will\n> adjust their behavior to match the man page.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nLooking really good.  Just some minor comments...\n\n> +git-switch(1)\n> +=============\n> +\n> +NAME\n> +----\n> +git-switch - Switch branches\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git switch' [<options>] [--guess] <branch>\n\nShould this now use [--no-guess] since --guess is the default?\n\n> +'git switch' [<options>] --detach [<start-point>]\n> +'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n> +\n> +DESCRIPTION\n> +-----------\n> +Switch to a specified branch. The working tree and the index are\n> +updated to match the branch. All new commits will be added to the tip\n> +of this branch.\n> +\n> +Optionally a new branch could be created with either `-c`, `-C`,\n> +automatically from a remote branch of same name (see `--guess`), or\n> +detach the working tree from any branch with `--detach`, along with\n> +switching.\n> +\n> +Switching branches does not require a clean index and working tree\n> +(i.e. no differences compared to `HEAD`). The operation is aborted\n> +however if the switch leads to loss of local changes, unless told\n> +otherwise.\n\nMaybe s/otherwise./otherwise with --discard-changes or --merge./, just\nfor a little extra clarity?\n\n> +-f::\n> +--force::\n> +       An alias for `--discard-changes`.\n> +\n> +--discard-changes::\n> +       Proceed even if the index or the working tree differs from\n> +       `HEAD`. Both the index and working tree are restored to match\n> +       the switching target. This is used to throw away local\n> +       changes.\n\nIt looks like elsewhere you and Junio discussed making --force also\nimply --ignore-in-progress.  That option should be moved close to\n--force, so that similar options are adjacent, but it also brings up a\nquestion for me:\n\nIs --force an alias for both `--discard-changes` and\n`--ignore-in-progress`, or is `--discard-changes` really just another\nname for `--force` (i.e. does it too imply `--ignore-in-progress`)?\nI'd be tempted to say the former, but I'm curious on others' thoughts.\n\n> +--orphan <new-branch>::\n> +       Create a new 'orphan' branch, named `<new-branch>`. If\n> +       `<start-point>` is specified, the index and working tree are\n> +       adjusted to match it. Otherwise both are adjusted to contain no\n> +       tracked files.\n\nThanks.  I'm still slightly hesitant about whether <start-point>\nshould be allowed with --orphan; it seems equivalent to me to letting\npeople have a flag for switching to existing branch A while forcing\nthe index and working tree to match branch B (defaulting B to HEAD\nfrom before the switch).  Having <start-point> and --orphan together\nis just a special case of this idea, and thus allowing those together\nseems like it'll cause the more general request to be filed at some\npoint, and we will already have the precedent of supporting it\nsomewhere.  This usecase seems to be somewhat esoteric and infrequent,\nand could be easily obtained by combining other commands.  I'm worried\nthat trying to explain this usecase may make the documentation for the\ncommon everyday commands even more complex, and these manpages are\nalready kind of long.  However, as I said above, that's just a slight\nhesitation and maybe I'm just excessively worried about the length of\nour manpages for the most common commands.  This new description of\n--orphan at least gets the default behavior right.\n\n\nGreat work so far; thanks for working on this.\n\nElijah\n"},{"id":"371837","messageId":"CABPp-BHHhQH9yb6h714vCAjnfyEbxtNUJoja1Ym1f5B88yfgWA@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqr2b56ir6.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-18T19:10:02Z","receivedAt":"2019-03-18T19:10:17Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Mar 17, 2019 at 7:03 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > I don't see why <start-point> even makes sense to use with --orphan;\n> > you should error if both are given, IMO.  The point of --orphan is to\n> > create some entirely new history.  So, I'd expect \"git switch --orphan\n> > <new-branch>\" to:\n> >   * not create refs/heads/<new-branch>\n> >   * set HEAD to refs/heads/<new-branch>\n> >   * empty all tracked files from the working tree.\n> >   * empty the index\n> >\n> > Alternatively, you could allow <start-point> to be passed with\n> > --orphan, adjusting the above steps so that both the index and the\n> > working tree are switched to match <start-point>, but ONLY if\n> > <start-point> defaults to the empty tree when --orphan is passed.\n>\n> Do you mean that it's like <start-point> is not really a start-point\n> but is an initial tree, i.e.\n>\n>         switch --orphan --initial-tree=<tree-ish> <new-branch>\n>\n> is a mere short-hand for\n>\n>         switch --orphan <new-branch> &&\n>         restore --from-tree=<tree-ish> .\n\nYes.\n\n> I think that does make sense, but at the same time, I think a major\n> reason why people say \"checkout does too many things depending on\n> the arguments and conext to be easily explained\" is exactly due to\n> its many \"if you give X, it is like writing this longer command\n> sequence\" short hands, so...\n\nYes, this is a concern for me too.  I would be happier if we made\n--orphan and <start-point> incompatible and avoided the need to\nexplain how they worked together.  Besides, as you point out, the\nwording is bad and should instead be a separate option named\n--initial-tree=<tree-ish> which people will then start asking us to\nallow them to specify even in cases when --orphan isn't (e.g. `git\nswitch --initial-tree=HEAD maint`), which is a weird/esoteric usecase\nthat is probably better served by using separate commands.\n\n> > How about:\n> >\n> > \"\"\"\n> > Switching and creating branches always involves knowing the\n> > <start-point> to begin the new branch from.  Sometimes, people want to\n> > create a new branch that does not have any commits yet; --orphan is a\n> > flag to allow that.  --orphan overrides the default of HEAD for\n> > <start-point> instead causing us to start from an empty history.  The\n> > use of --orphan is incompatible with specifying a <start-point>.\n> > \"\"\"\n>\n> With or without the short-hand to say which initial tree to populate\n> the index from, the above description makes sense to me.\n\nYeah, perhaps \"...an empty history with all tracked files removed from\nthe index and working tree.  The use of...\"\n"},{"id":"371861","messageId":"CAPig+cSc2tbOhjxrOiK_+1M7RdwbXL5-d-eCPm6gk84ESE1xpA@mail.gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-15-pclouds@gmail.com","subject":"Re: [PATCH v4 14/26] switch: add --discard-changes","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-18T23:55:13Z","receivedAt":"2019-03-18T23:55:28Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 17, 2019 at 8:51 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> --discard-changes is a better name than --force for this option since\n> it's what really happens. --force is turned to an alias for\n> --discard-changes. But it's meant to an alias for potentially more force\n\ns/to an/to be an/\n\n> options in the future.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"},{"id":"371865","messageId":"xmqq4l7zzoal.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8B+VmwygpxWQ9Ug=L8bQXDVX7h6MjJwVU1cR2sbuHo6qQ@mail.gmail.com","subject":"Re: [PATCH v4 00/26] Add new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-19T00:43:46Z","receivedAt":"2019-03-19T00:43:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Mon, Mar 18, 2019 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:\n>> I often find \"git reset --hard <some other starting point>\" after a\n>\n> It still bugs me that I need to use this to abort some in-progress\n> operation.\n\nNote that my comment was *NOT* about aborting the in-progress \"am\".\nWhat happens in that \"I often find\" situation is that I identify the\nproblem's origin, prepare a branch to apply a fix forking from an\nold release point that includes the origin, and \"am\" tells me the\ncode diverged too much and it cannot be applied, or \"am -3\" tells me\nthat the author sent the patch based on some unknown base.  At that\npoint, I \"git reset --hard\" to some plausible place the author would\nhave based the patch (e.g. \"master\" pushed out last night) and then\nsay \"am\" or (\"am --no-3\") to retry.\n\nSo your response is unrelated to what I said, but having said that,\nit still deserves a comment.\n\n> ... There's \"git X --abort\" but I would need to find out what X\n> is first. I would like \"git abort\" or something (and \"git continue\")\n> but haven't really thought that through yet. That direction may also\n> lead to \"git undo\" which is another can of worms.\n\nI rarely had \"what was I doing\" problem; doesn't the command line\nprompt tell you?  It is debatable (and I am pessimistic) if a\ngeneral blanket \"git abort\" makes sense, but the info on the current\nstatus (i.e. \"what was the user doing?\") it must gather should be\na strict superset as what is needed to give useful info in the\ncommand line prompt, so a good place to start may be to think about\nwhat's missing (not shown) in the prompt and if that is deliberate\nomission (avoiding overly verbose prompt) or is coming from lack of\nenough information.  Then we may need to do something about the latter\nby teaching these commands with state to leave more clue of what we\nwere in the middle of and what the next step would be.\n\nAfter thinking about that, the answer might turn out to be to unify\nthe backend of these commands with state to a single one, or at\nleast fewer ones, so that we can have better grasp and control over\nhow these backend engines record state when they give control back\nto the user.\n\n\n"},{"id":"371897","messageId":"20190319093910.20229-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"78628256-79dc-3036-c57b-a96797ceb120@gmail.com","subject":"[PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-19T09:39:10Z","receivedAt":"2019-03-19T09:39:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If you have staged changes in path A and perform 'checkout\n--merge' (which could result in conflicts in a totally unrelated path\nB), changes in A will be gone. Which is unexpected. We are supposed\nto keep all changes, or kick and scream otherwise.\n\nThis is the result of how --merge is implemented, from the very first\nday in 1be0659efc (checkout: merge local modifications while switching\nbranches., 2006-01-12):\n\n1. a merge is done, unmerged entries are collected\n2. a hard switch to a new branch is done, then unmerged entries added\n   back\n\nThere is no trivial fix for this. Going with 3-way merge one file at a\ntime loses rename detection. Going with 3-way merge by trees requires\nteaching the algorithm to pick up staged changes. And even if we detect\nstaged changes with --merge and abort for safety, an option to continue\n--merge is very weird. Such an option would keep worktree changes, but\ndrop staged changes.\n\nBecause the problem has been with us since the introduction of --merge\nand everybody has been pretty happy (except Phillip, who found this\nproblem), I'll just take a note here to acknowledge it and wait for\nmerge wizards to come in and work their magic. There may be a way\nforward [1].\n\n[1] CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com\n\nReported-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This is my \"fix\" for Phillip's second problem. I chose to reply here\n because this is where an actual fix was discussed. The test script to\n demonstate it is here\n\n https://public-inbox.org/git/7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com/\n\n Documentation/git-checkout.txt | 2 ++\n builtin/checkout.c             | 9 +++++++++\n 2 files changed, 11 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f179b43732..877e5f503a 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -242,6 +242,8 @@ should result in deletion of the path).\n +\n When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n++\n+When switching branches with `--merge`, staged changes may be lost.\n \n --conflict=<style>::\n \tThe same as --merge option above, but changes the way the\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e6037b296..f95e7975f7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\tstruct tree *result;\n \t\t\tstruct tree *work;\n \t\t\tstruct merge_options o;\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\n \t\t\tif (!opts->merge)\n \t\t\t\treturn 1;\n \n@@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\tif (!old_branch_info->commit)\n \t\t\t\treturn 1;\n \n+\t\t\tif (repo_index_has_changes(the_repository,\n+\t\t\t\t\t\t   get_commit_tree(old_branch_info->commit),\n+\t\t\t\t\t\t   &sb))\n+\t\t\t\twarning(_(\"staged changes in the following files may be lost: %s\"),\n+\t\t\t\t\tsb.buf);\n+\t\t\tstrbuf_release(&sb);\n+\n \t\t\t/* Do more real merge */\n \n \t\t\t/*\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"371900","messageId":"f9e3bd68-935c-3bca-6d06-66f51ba15451@gmail.com","threadId":"50360","inReplyTo":"20190319093910.20229-1-pclouds@gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-19T11:24:53Z","receivedAt":"2019-03-19T11:24:59Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Duy\n\nOn 19/03/2019 09:39, Nguyễn Thái Ngọc Duy wrote:\n> If you have staged changes in path A and perform 'checkout\n> --merge' (which could result in conflicts in a totally unrelated path\n> B), changes in A will be gone. Which is unexpected. We are supposed\n> to keep all changes, or kick and scream otherwise.\n> \n> This is the result of how --merge is implemented, from the very first\n> day in 1be0659efc (checkout: merge local modifications while switching\n> branches., 2006-01-12):\n> \n> 1. a merge is done, unmerged entries are collected\n> 2. a hard switch to a new branch is done, then unmerged entries added\n>     back\n> \n> There is no trivial fix for this. Going with 3-way merge one file at a\n> time loses rename detection. Going with 3-way merge by trees requires\n> teaching the algorithm to pick up staged changes. And even if we detect\n> staged changes with --merge and abort for safety, an option to continue\n> --merge is very weird. Such an option would keep worktree changes, but\n> drop staged changes.\n> \n> Because the problem has been with us since the introduction of --merge\n> and everybody has been pretty happy (except Phillip, who found this\n> problem), I'll just take a note here to acknowledge it and wait for\n> merge wizards to come in and work their magic. There may be a way\n> forward [1].\n> \n> [1] CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com\n> \n> Reported-by: Phillip Wood <phillip.wood123@gmail.com>\n\nI try to use phillip.wood@dunelm.org.uk for git stuff as it shouldn't \nchange in the future.\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>   This is my \"fix\" for Phillip's second problem. I chose to reply here\n>   because this is where an actual fix was discussed. The test script to\n>   demonstate it is here\n> \n>   https://public-inbox.org/git/7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com/\n> \n>   Documentation/git-checkout.txt | 2 ++\n>   builtin/checkout.c             | 9 +++++++++\n>   2 files changed, 11 insertions(+)\n> \n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> index f179b43732..877e5f503a 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -242,6 +242,8 @@ should result in deletion of the path).\n>   +\n>   When checking out paths from the index, this option lets you recreate\n>   the conflicted merge in the specified paths.\n> ++\n> +When switching branches with `--merge`, staged changes may be lost.\n>   \n>   --conflict=<style>::\n>   \tThe same as --merge option above, but changes the way the\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 0e6037b296..f95e7975f7 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>   \t\t\tstruct tree *result;\n>   \t\t\tstruct tree *work;\n>   \t\t\tstruct merge_options o;\n> +\t\t\tstruct strbuf sb = STRBUF_INIT;\n> +\n>   \t\t\tif (!opts->merge)\n>   \t\t\t\treturn 1;\n>   \n> @@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>   \t\t\tif (!old_branch_info->commit)\n>   \t\t\t\treturn 1;\n>   \n> +\t\t\tif (repo_index_has_changes(the_repository,\n> +\t\t\t\t\t\t   get_commit_tree(old_branch_info->commit),\n> +\t\t\t\t\t\t   &sb))\n> +\t\t\t\twarning(_(\"staged changes in the following files may be lost: %s\"),\n> +\t\t\t\t\tsb.buf);\n> +\t\t\tstrbuf_release(&sb);\n\nThanks for doing this, I think having some sort of warning is a good \nidea, I wonder if this could be quite noisy though. I guess it depends \non how many staged changes people have that don't match the new index. \nIf we diff against the new tree and only print names that are in both \nlists does that give a definitive list of what will be lost? If it does \nthen if there are a lot of files affected then it will still be noisy \n(using columns may help) but at least it will not contain false \npositives. It is more work though, maybe we should just say \"staged \nchanges may be lost\" and leave it at that.\n\nBest Wishes\n\nPhillip\n\n> +\n>   \t\t\t/* Do more real merge */\n>   \n>   \t\t\t/*\n> \n"},{"id":"371904","messageId":"228d681e-4cfd-7e2e-8bb9-1624cc244a9f@gmail.com","threadId":"50360","inReplyTo":"20190318113822.6195-1-pclouds@gmail.com","subject":"Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-19T14:06:44Z","receivedAt":"2019-03-19T14:06:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Duy\n\nThanks for doing this, one minor comment - I try to use \nphillip.wood@dunelm.org.uk for git as it wont change if I change my \nemail provider.\n\nBest Wishes\n\nPhillip\n\nOn 18/03/2019 11:38, Nguyễn Thái Ngọc Duy wrote:\n> Phillip found out that 'git checkout -f <branch>' does not restore\n> conflict/unmerged files correctly. All tracked files should be taken\n> from <branch> and all non-zero stages removed. Most of this is true,\n> except that the final file could be in stage one instead of zero.\n> \n> \"checkout -f\" (among other commands) does this with one-way merge, which\n> is supposed to take stat info from the index and everything else from\n> the given tree. The add_entry(.., old, ...) call in oneway_merge()\n> though will keep stage index from the index.\n> \n> This is normally not a problem if the entry from the index is\n> normal (stage #0). But if there is a conflict, stage #0 does not exist\n> and we'll get stage #1 entry as \"old\" variable, which gets recorded in\n> the final index. Fix it by clearing stage mask.\n> \n> This bug probably comes from b5b425074e (git-read-tree: make one-way\n> merge also honor the \"update\" flag, 2005-06-07). Before this commit, we\n> may create the final (\"dst\") index entry from the one in index, but we\n> do clear CE_STAGEMASK.\n> \n> I briefly checked two- and three-way merge functions. I think we don't\n> have the same problem in those.\n> \n> Reported-by: Phillip Wood <phillip.wood123@gmail.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>   v2 updates log message to describe the problem and moves the test to\n>   t2023-checkout.m.sh\n> \n>   t/t2023-checkout-m.sh | 24 ++++++++++++++++++++++++\n>   unpack-trees.c        |  2 +-\n>   2 files changed, 25 insertions(+), 1 deletion(-)\n> \n> diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh\n> index 7e18985134..fca3f85824 100755\n> --- a/t/t2023-checkout-m.sh\n> +++ b/t/t2023-checkout-m.sh\n> @@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '\n>   \ttest_cmp both.txt.conflicted.cleaned both.txt.cleaned\n>   '\n>   \n> +test_expect_success 'force checkout a conflict file creates stage zero entry' '\n> +\tgit init co-force &&\n> +\t(\n> +\t\tcd co-force &&\n> +\t\techo a >a &&\n> +\t\tgit add a &&\n> +\t\tgit commit -ama &&\n> +\t\tA_OBJ=$(git rev-parse :a) &&\n> +\t\tgit branch topic &&\n> +\t\techo b >a &&\n> +\t\tgit commit -amb &&\n> +\t\tB_OBJ=$(git rev-parse :a) &&\n> +\t\tgit checkout topic &&\n> +\t\techo c >a &&\n> +\t\tC_OBJ=$(git hash-object a) &&\n> +\t\tgit checkout -m master &&\n> +\t\ttest_cmp_rev :1:a $A_OBJ &&\n> +\t\ttest_cmp_rev :2:a $B_OBJ &&\n> +\t\ttest_cmp_rev :3:a $C_OBJ &&\n> +\t\tgit checkout -f topic &&\n> +\t\ttest_cmp_rev :0:a $A_OBJ\n> +\t)\n> +'\n> +\n>   test_done\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 22c41a3ba8..1ccd343cad 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -2386,7 +2386,7 @@ int oneway_merge(const struct cache_entry * const *src,\n>   \t\tif (o->update && S_ISGITLINK(old->ce_mode) &&\n>   \t\t    should_update_submodules() && !verify_uptodate(old, o))\n>   \t\t\tupdate |= CE_UPDATE;\n> -\t\tadd_entry(o, old, update, 0);\n> +\t\tadd_entry(o, old, update, CE_STAGEMASK);\n>   \t\treturn 0;\n>   \t}\n>   \treturn merged_entry(a, old, o);\n> \n"},{"id":"371936","messageId":"xmqqimwexujm.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190319093910.20229-1-pclouds@gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-20T00:23:57Z","receivedAt":"2019-03-20T00:24:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> If you have staged changes in path A and perform 'checkout\n> --merge' (which could result in conflicts in a totally unrelated path\n> B), changes in A will be gone. Which is unexpected. We are supposed\n> to keep all changes, or kick and scream otherwise.\n>\n> This is the result of how --merge is implemented, from the very first\n> day in 1be0659efc (checkout: merge local modifications while switching\n> branches., 2006-01-12):\n>\n> 1. a merge is done, unmerged entries are collected\n> 2. a hard switch to a new branch is done, then unmerged entries added\n>    back\n>\n> There is no trivial fix for this. Going with 3-way merge one file at a\n> time loses rename detection. Going with 3-way merge by trees requires\n> teaching the algorithm to pick up staged changes. And even if we detect\n> staged changes with --merge and abort for safety, an option to continue\n> --merge is very weird. Such an option would keep worktree changes, but\n> drop staged changes.\n\nI think \"checkout -m <otherbranch>\" with a dirty index should refuse\nto run; there is nothing to \"continue\" after such a failure, so I am\nnot sure what you mean by \"an option to continue\" (iow, I do not see\na need for such an option, and if that option makes the whole notion\nstrange, we can just decide not to have it, can't we?).\n"},{"id":"371938","messageId":"xmqq5zsextqq.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"228d681e-4cfd-7e2e-8bb9-1624cc244a9f@gmail.com","subject":"Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-20T00:41:17Z","receivedAt":"2019-03-20T00:41:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Thanks for doing this, one minor comment - I try to use\n> phillip.wood@dunelm.org.uk for git as it wont change if I change my\n> email provider.\n\nYou mean something like this?\n\n .mailmap | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/.mailmap b/.mailmap\nindex 82cd0569d5..6e137576ec 100644\n--- a/.mailmap\n+++ b/.mailmap\n@@ -212,6 +212,7 @@ Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>\n Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>\n Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>\n Philippe Bruhat <book@cpan.org>\n+Phillip Wood <phillip.wood@dunelm.org.uk> <phillip.wood123@gmail.com>\n Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>\n Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>\n Randall S. Becker <randall.becker@nexbridge.ca> <rsbecker@nexbridge.com>\n"},{"id":"371939","messageId":"CACsJy8BLxWea0ZrHkSU6+nE7dr5YvFNVH1tLhaqPRadi+2Hhwg@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqimwexujm.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-20T00:40:54Z","receivedAt":"2019-03-20T00:41:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 20, 2019 at 7:24 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > If you have staged changes in path A and perform 'checkout\n> > --merge' (which could result in conflicts in a totally unrelated path\n> > B), changes in A will be gone. Which is unexpected. We are supposed\n> > to keep all changes, or kick and scream otherwise.\n> >\n> > This is the result of how --merge is implemented, from the very first\n> > day in 1be0659efc (checkout: merge local modifications while switching\n> > branches., 2006-01-12):\n> >\n> > 1. a merge is done, unmerged entries are collected\n> > 2. a hard switch to a new branch is done, then unmerged entries added\n> >    back\n> >\n> > There is no trivial fix for this. Going with 3-way merge one file at a\n> > time loses rename detection. Going with 3-way merge by trees requires\n> > teaching the algorithm to pick up staged changes. And even if we detect\n> > staged changes with --merge and abort for safety, an option to continue\n> > --merge is very weird. Such an option would keep worktree changes, but\n> > drop staged changes.\n>\n> I think \"checkout -m <otherbranch>\" with a dirty index should refuse\n> to run; there is nothing to \"continue\" after such a failure, so I am\n> not sure what you mean by \"an option to continue\" (iow, I do not see\n> a need for such an option, and if that option makes the whole notion\n> strange, we can just decide not to have it, can't we?).\n\nWe have --force to continue even when we have local changes, which\nwill be overwritten. I was thinking a similar option which gives us\npermission to destroy staged changes.\n\nRefusing to run fails the test suite though (I tried that even before\nthis patch), in t7201.10, \"switch to another branch while carrying a\ndeletion\", because of this line\n\n    git rm two\n\n-- \nDuy\n"},{"id":"371944","messageId":"xmqqk1guwdeu.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8BLxWea0ZrHkSU6+nE7dr5YvFNVH1tLhaqPRadi+2Hhwg@mail.gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-20T01:19:21Z","receivedAt":"2019-03-20T01:19:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> I think \"checkout -m <otherbranch>\" with a dirty index should refuse\n>> to run; there is nothing to \"continue\" after such a failure, so I am\n>> not sure what you mean by \"an option to continue\" (iow, I do not see\n>> a need for such an option, and if that option makes the whole notion\n>> strange, we can just decide not to have it, can't we?).\n>\n> We have --force to continue even when we have local changes, which\n> will be overwritten. I was thinking a similar option which gives us\n> permission to destroy staged changes.\n\nAh, then that is not \"checkout --continue\", but \"checkout --force\n-m\"?  That sounds sensible, and should behave as if \"checkout -f\nHEAD && checkout -m <otherbranch>\" was done, with respect to local\nchanges, I would think.\n"},{"id":"371945","messageId":"CACsJy8AUH4nOkt6H=yu=eRktK4me9kEkjqsC3zyNZDwUKXqCPg@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqk1guwdeu.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-20T01:22:22Z","receivedAt":"2019-03-20T01:22:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 20, 2019 at 8:19 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> >> I think \"checkout -m <otherbranch>\" with a dirty index should refuse\n> >> to run; there is nothing to \"continue\" after such a failure, so I am\n> >> not sure what you mean by \"an option to continue\" (iow, I do not see\n> >> a need for such an option, and if that option makes the whole notion\n> >> strange, we can just decide not to have it, can't we?).\n> >\n> > We have --force to continue even when we have local changes, which\n> > will be overwritten. I was thinking a similar option which gives us\n> > permission to destroy staged changes.\n>\n> Ah, then that is not \"checkout --continue\", but \"checkout --force\n> -m\"?  That sounds sensible, and should behave as if \"checkout -f\n> HEAD && checkout -m <otherbranch>\" was done, with respect to local\n> changes, I would think.\n\nKinda. But \"--force --merge\" makes no sense. --force discards all\nlocal changes by definition, which means you can't have conflicts and\nwill not need --merge. I think this is the reason why we die() out\nwhen both are specified. So we need something like\n--discard-staged-changes-only...\n-- \nDuy\n"},{"id":"371946","messageId":"xmqqftriwbz6.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CACsJy8AUH4nOkt6H=yu=eRktK4me9kEkjqsC3zyNZDwUKXqCPg@mail.gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-20T01:50:21Z","receivedAt":"2019-03-20T01:50:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Kinda. But \"--force --merge\" makes no sense. --force discards all\n> local changes by definition, which means you can't have conflicts and\n> will not need --merge. I think this is the reason why we die() out\n> when both are specified. So we need something like\n> --discard-staged-changes-only...\n\nAt that point, I would have to say that we do not need anything.\nThe use case is already covered with \"git reset && git checkout -m\",\nisn't it?\n\nThanks.\n\n"},{"id":"371973","messageId":"CACsJy8ANaKRz04it93Xpodz1wZfUjsbZXbMfrUSLvtfWbZGQWw@mail.gmail.com","threadId":"50360","inReplyTo":"xmqq5zsextqq.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2] unpack-trees: fix oneway_merge accidentally carry over stage index","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-20T09:50:53Z","receivedAt":"2019-03-20T09:51:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 20, 2019 at 7:41 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n>\n> > Thanks for doing this, one minor comment - I try to use\n> > phillip.wood@dunelm.org.uk for git as it wont change if I change my\n> > email provider.\n>\n> You mean something like this?\n\nI think he meant fixing the Reported-by: line. But since there's at\nleast another commit in 'next' with the 123@gmail address, it's\nprobably best to update .mailmap too (and maybe still fix the\nReported-by: in this patch).\n\n>  .mailmap | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/.mailmap b/.mailmap\n> index 82cd0569d5..6e137576ec 100644\n> --- a/.mailmap\n> +++ b/.mailmap\n> @@ -212,6 +212,7 @@ Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>\n>  Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>\n>  Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>\n>  Philippe Bruhat <book@cpan.org>\n> +Phillip Wood <phillip.wood@dunelm.org.uk> <phillip.wood123@gmail.com>\n>  Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>\n>  Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>\n>  Randall S. Becker <randall.becker@nexbridge.ca> <rsbecker@nexbridge.com>\n\n\n\n-- \nDuy\n"},{"id":"371976","messageId":"83ddb0c1-26f4-f1ff-3d26-543d8fee0bce@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-23-pclouds@gmail.com","subject":"Re: [PATCH v4 22/26] switch: reject if some operation is in progress","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-20T11:16:55Z","receivedAt":"2019-03-20T11:17:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Duy\n\nThe new switch command is shaping up nicely\n\nOn 17/03/2019 12:49, Nguyễn Thái Ngọc Duy wrote:\n> Unless you know what you're doing, switching to another branch to do\n> something then switching back could be confusing. Worse, you may even\n> forget that you're in the middle of something. By the time you realize,\n> you may have done a ton of work and it gets harder to go back.\n> \n> The new option --ignore-in-progress is added to override this check.\n> However it's probably just safer to create a separate worktree for\n> that separate thing you want to work on and leave this worktree\n> alone (unless of course creating or preparing worktrees are not cheap).\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 26 ++++++++++++++++++++++++++\n>  1 file changed, 26 insertions(+)\n> \n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 2aeaac092d..21a585eaa9 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -24,6 +24,7 @@\n>  #include \"tree.h\"\n>  #include \"tree-walk.h\"\n>  #include \"unpack-trees.h\"\n> +#include \"wt-status.h\"\n>  #include \"xdiff-interface.h\"\n>  \n>  static const char * const checkout_usage[] = {\n> @@ -56,6 +57,7 @@ struct checkout_opts {\n>  \tint accept_pathspec;\n>  \tint switch_branch_doing_nothing_is_ok;\n>  \tint only_merge_on_switching_branches;\n> +\tint can_switch_when_in_progress;\n>  \n>  \tconst char *new_branch;\n>  \tconst char *new_branch_force;\n> @@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,\n>  \t    !new_branch_info->path)\n>  \t\tdie_expecting_a_branch(new_branch_info);\n>  \n> +\tif (!opts->can_switch_when_in_progress) {\n> +\t\tstruct wt_status_state state;\n> +\n> +\t\tmemset(&state, 0, sizeof(state));\n> +\t\twt_status_get_state(the_repository, &state, 0);\n> +\n> +\t\tif (state.merge_in_progress)\n> +\t\t\tdie(_(\"cannot switch branch while merging\"));\n> +\t\tif (state.am_in_progress)\n> +\t\t\tdie(_(\"cannot switch branch in the middle of an am session\"));\n> +\t\tif (state.rebase_in_progress || state.rebase_in_progress)\n\nI think one of those should be state.rebase_interactive_in_progress\n\nBest Wishes\n\nPhillip\n\n> +\t\t\tdie(_(\"cannot switch branch while rebasing\"));\n> +\t\tif (state.cherry_pick_in_progress)\n> +\t\t\tdie(_(\"cannot switch branch while cherry-picking\"));\n> +\t\tif (state.revert_in_progress)\n> +\t\t\tdie(_(\"cannot switch branch while reverting\"));\n> +\t\tif (state.bisect_in_progress)\n> +\t\t\tdie(_(\"cannot switch branch while bisecting\"));\n> +\t}\n> +\n>  \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n>  \t    !opts->ignore_other_worktrees) {\n>  \t\tint flag;\n> @@ -1514,6 +1536,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \topts.only_merge_on_switching_branches = 0;\n>  \topts.accept_pathspec = 1;\n>  \topts.implicit_detach = 1;\n> +\topts.can_switch_when_in_progress = 1;\n>  \n>  \toptions = parse_options_dup(checkout_options);\n>  \toptions = add_common_options(&opts, options);\n> @@ -1539,6 +1562,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>  \t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n>  \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n>  \t\t\t N_(\"throw away local modifications\")),\n> +\t\tOPT_BOOL(0, \"ignore-in-progress\", &opts.can_switch_when_in_progress,\n> +\t\t\t N_(\"allow to switch branch when some operation is still in progress\")),\n>  \t\tOPT_END()\n>  \t};\n>  \tint ret;\n> @@ -1549,6 +1574,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>  \topts.switch_branch_doing_nothing_is_ok = 0;\n>  \topts.only_merge_on_switching_branches = 1;\n>  \topts.implicit_detach = 0;\n> +\topts.can_switch_when_in_progress = 0;\n>  \n>  \toptions = parse_options_dup(switch_options);\n>  \toptions = add_common_options(&opts, options);\n> \n\n"},{"id":"372002","messageId":"CACsJy8CLpC1bdZRfbUzw3J+WBc0ZV5WTOfXczLfaQ7vRMqZUdA@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BHHhQH9yb6h714vCAjnfyEbxtNUJoja1Ym1f5B88yfgWA@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-20T12:04:12Z","receivedAt":"2019-03-20T12:04:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 19, 2019 at 2:10 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Sun, Mar 17, 2019 at 7:03 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Elijah Newren <newren@gmail.com> writes:\n> >\n> > > I don't see why <start-point> even makes sense to use with --orphan;\n> > > you should error if both are given, IMO.  The point of --orphan is to\n> > > create some entirely new history.  So, I'd expect \"git switch --orphan\n> > > <new-branch>\" to:\n> > >   * not create refs/heads/<new-branch>\n> > >   * set HEAD to refs/heads/<new-branch>\n> > >   * empty all tracked files from the working tree.\n> > >   * empty the index\n> > >\n> > > Alternatively, you could allow <start-point> to be passed with\n> > > --orphan, adjusting the above steps so that both the index and the\n> > > working tree are switched to match <start-point>, but ONLY if\n> > > <start-point> defaults to the empty tree when --orphan is passed.\n> >\n> > Do you mean that it's like <start-point> is not really a start-point\n> > but is an initial tree, i.e.\n> >\n> >         switch --orphan --initial-tree=<tree-ish> <new-branch>\n> >\n> > is a mere short-hand for\n> >\n> >         switch --orphan <new-branch> &&\n> >         restore --from-tree=<tree-ish> .\n>\n> Yes.\n>\n> > I think that does make sense, but at the same time, I think a major\n> > reason why people say \"checkout does too many things depending on\n> > the arguments and conext to be easily explained\" is exactly due to\n> > its many \"if you give X, it is like writing this longer command\n> > sequence\" short hands, so...\n>\n> Yes, this is a concern for me too.  I would be happier if we made\n> --orphan and <start-point> incompatible and avoided the need to\n> explain how they worked together.  Besides, as you point out, the\n> wording is bad and should instead be a separate option named\n> --initial-tree=<tree-ish> which people will then start asking us to\n> allow them to specify even in cases when --orphan isn't (e.g. `git\n> switch --initial-tree=HEAD maint`), which is a weird/esoteric usecase\n> that is probably better served by using separate commands.\n\nOK let's reject '--orphan <new-branch> <initial-tree>' then. It's not\nmuch work and when people come back complaining about it not working,\nwe'll know more and may reconsider then.\n-- \nDuy\n"},{"id":"372005","messageId":"CABPp-BFB0jafua0CGEsr66EzQvHncLYvws8rt+LdkQ_LXcEq5A@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqftriwbz6.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-20T13:53:01Z","receivedAt":"2019-03-20T13:53:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 19, 2019 at 7:50 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> > Kinda. But \"--force --merge\" makes no sense. --force discards all\n> > local changes by definition, which means you can't have conflicts and\n> > will not need --merge. I think this is the reason why we die() out\n> > when both are specified. So we need something like\n> > --discard-staged-changes-only...\n>\n> At that point, I would have to say that we do not need anything.\n> The use case is already covered with \"git reset && git checkout -m\",\n> isn't it?\n\nI guess the problem is just that 'git checkout -m' has not refused to\nrun with either a dirty index or a dirty working tree, and if both are\ndirty (making us require more of a four-way merge), then our three-way\nmerge has to have some kind of casualty in the implementation for at\nleast some case.  The current casualty as highlighted by Philip is\nthat newly staged files before the 'checkout -m' become untracked and\nany carefully staged pieces before that command are lost amongst the\nunstaged changes again even if there weren't any conflicts.\n\nOne solution is to just accept and document or warn about this\nshortcoming for now as Duy did in his patch.  Another is to do as you\nmentioned earlier in this thread when you stated 'I think \"checkout -m\n<otherbranch>\" with a dirty index should refuse to run'.  Duy linked\nto a third option that I outlined in his commit message, though it'd\nrequire a bit more capability from merge-recursive than we have today.\n\nSo, I think we do need something (eventually at least).  Would you\nprefer we dropped this patch from Duy and instead made 'checkout -m'\nabort when the index is dirty?\n"},{"id":"372006","messageId":"CACsJy8BdPgPgaWW=3_y5peLdqo24-ozC=s90WW2DN9OsYe9v_w@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BFB0jafua0CGEsr66EzQvHncLYvws8rt+LdkQ_LXcEq5A@mail.gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-20T13:57:54Z","receivedAt":"2019-03-20T13:58:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 20, 2019 at 8:53 PM Elijah Newren <newren@gmail.com> wrote:\n> So, I think we do need something (eventually at least).  Would you\n> prefer we dropped this patch from Duy and instead made 'checkout -m'\n> abort when the index is dirty?\n\nI have no problem with this. Still scratching my head wondering if\nt7201-co.sh has a slightly incorrect setup, or aborting is actually\nwrong. You're probably a better person to understand that test case\n;-)\n-- \nDuy\n"},{"id":"372042","messageId":"xmqqimwduknl.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CABPp-BFB0jafua0CGEsr66EzQvHncLYvws8rt+LdkQ_LXcEq5A@mail.gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-21T00:38:06Z","receivedAt":"2019-03-21T00:38:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Tue, Mar 19, 2019 at 7:50 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> Duy Nguyen <pclouds@gmail.com> writes:\n>>\n>> > Kinda. But \"--force --merge\" makes no sense. --force discards all\n>> > local changes by definition, which means you can't have conflicts and\n>> > will not need --merge. I think this is the reason why we die() out\n>> > when both are specified. So we need something like\n>> > --discard-staged-changes-only...\n>>\n>> At that point, I would have to say that we do not need anything.\n>> The use case is already covered with \"git reset && git checkout -m\",\n>> isn't it?\n>\n> I guess the problem is just that 'git checkout -m' has not refused to\n> run with either a dirty index or a dirty working tree, and if both are\n> dirty (making us require more of a four-way merge), then our three-way\n> merge has to ...\n\nI didn't actually mean \"nothing to do here\" relative to the current\ncode; instead, I meant \"nothing more than just stop when the index\nhas updates\" (which is hard to read from the above quoted part, as\n\"Kinda.\" is a response in a discussion started with my \"checkout -m\nshould probably refuse to do anything when the index is dirty\").\n\n> So, I think we do need something (eventually at least).  Would you\n> prefer we dropped this patch from Duy and instead made 'checkout -m'\n> abort when the index is dirty?\n\nLet's go with the doc update first, as the patch has already\nwritten.  I think in the longer term, just aborting when the index\nis dirty would be a vast improvement over the status quo + a doc\nupdate and is a good place to stop.\n\nThanks.\n"},{"id":"372094","messageId":"20190321131655.15249-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190317124926.17137-1-pclouds@gmail.com","subject":"[PATCH v5 00/26] Add new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:29Z","receivedAt":"2019-03-21T13:17:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This adds a new command 'git-switch' as the half-replacement for\n'git-checkout'. Jump to 12/26 as the starting point. The other half is\ngit-restore, which is dealt with separately.\n\nThe diff delta is shrinking nicely. The two main changes are\n\n- '--orphan <new-branch> <initial-tree>' is no longer accepted\n- --force implies --ignore-in-progress\n\nThe rest is the usual typo and bug fixes.\n\nThe two \"todo\" items from v4 will be handled separately and will not\nbe mentioned again in v6 (if there is one):\n\n - The 'checkout -m' losing staged changes problem has already been\ndiscussed elsewhere and I'm still trying to see if I can improve it.\n\n - I have some work in progress for improving tracking branch error\nmessage, but it's unlikely to conflict badly with this series.\n\nNguyễn Thái Ngọc Duy (26):\n  git-checkout.txt: spell out --no-option\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  git-checkout.txt: fix monospace typeset\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: advice how to get out of detached HEAD mode\n  checkout: keep most #include sorted\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command 'switch'\n  switch: better names for -b and -B\n  switch: add --discard-changes\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: implicit dwim, use --no-guess to disable it\n  switch: no worktree status unless real branch switch happens\n  switch: reject if some operation is in progress\n  switch: make --orphan switch to an empty tree\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |  12 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                | 221 ++++---\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   5 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt (new)            | 276 ++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  19 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/gitworkflows.txt                |   3 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  56 +-\n Makefile                                      |   1 +\n advice.c                                      |  17 +-\n builtin.h                                     |   1 +\n builtin/checkout.c                            | 605 ++++++++++--------\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  37 +-\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   1 +\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |  28 +-\n t/t2060-switch.sh (new +x)                    |  98 +++\n 39 files changed, 1051 insertions(+), 493 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n\nRange-diff dựa trên v4:\n 1:  535dc1f310 !  1:  7bcb4b0ff8 doc: document --overwrite-ignore\n    @@ -22,20 +22,7 @@\n     +\n      --recurse-submodules::\n      --no-recurse-submodules::\n    --\tUsing --recurse-submodules will update the content of all initialized\n    -+\tUsing `--recurse-submodules` will update the content of all initialized\n    - \tsubmodules according to the commit recorded in the superproject. If\n    - \tlocal modifications in a submodule would be overwritten the checkout\n    --\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n    -+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n    - \tis used, the work trees of submodules will not be updated.\n    --\tJust like linkgit:git-submodule[1], this will detach the\n    --\tsubmodules HEAD.\n    -+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    -+\tsubmodule.\n    - \n    - --no-guess::\n    - \tDo not attempt to create a branch if a remote tracking branch\n    + \tUsing --recurse-submodules will update the content of all initialized\n     \n      diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\n      --- a/Documentation/git-merge.txt\n 2:  b6305d2871 !  2:  f2d77152eb git-checkout.txt: fix monospace typeset\n    @@ -4,8 +4,7 @@\n     \n         Add backticks where we have none, replace single quotes with backticks\n         and replace double-quotes. Drop double-quotes from nested constructions\n    -    such as `\"@{-1}\"`. Add a missing possessive apostrophe after the word\n    -    \"submodules\" while at it.\n    +    such as `\"@{-1}\"`.\n     \n         Helped-by: Martin Ågren <martin.agren@gmail.com>\n         Signed-off-by: Martin Ågren <martin.agren@gmail.com>\n    @@ -207,6 +206,24 @@\n      This means that you can use `git checkout -p` to selectively discard\n      edits from your current working tree. See the ``Interactive Mode''\n     @@\n    + \n    + --recurse-submodules::\n    + --no-recurse-submodules::\n    +-\tUsing --recurse-submodules will update the content of all initialized\n    ++\tUsing `--recurse-submodules` will update the content of all initialized\n    + \tsubmodules according to the commit recorded in the superproject. If\n    + \tlocal modifications in a submodule would be overwritten the checkout\n    +-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n    ++\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n    + \tis used, the work trees of submodules will not be updated.\n    +-\tJust like linkgit:git-submodule[1], this will detach the\n    +-\tsubmodules HEAD.\n    ++\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    ++\tsubmodule.\n    + \n    + --no-guess::\n    + \tDo not attempt to create a branch if a remote tracking branch\n    +@@\n      \tIn the default overlay mode, `git checkout` never\n      \tremoves files from the index or the working tree.  When\n      \tspecifying `--no-overlay`, files that appear in the index and\n 3:  bb56e45457 =  3:  cbe630fb4a t: rename t2014-switch.sh to t2014-checkout-switch.sh\n 4:  123392757a =  4:  6bc8a1052f checkout: advice how to get out of detached HEAD mode\n 5:  d1ec6b4ce0 =  5:  0bb2aefb85 checkout: keep most #include sorted\n 6:  4b1742672b =  6:  ace82aa26f checkout: factor out some code in parse_branchname_arg()\n 7:  e0bcc3a4dd =  7:  6e13efcba8 checkout: make \"opts\" in cmd_checkout() a pointer\n 8:  ca5b4d7db0 =  8:  f61a042eb8 checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n 9:  13c03997f0 =  9:  972cebc568 checkout: split options[] array in three pieces\n10:  24d532b276 ! 10:  970c727b24 checkout: split part of it to new command 'switch'\n    @@ -147,9 +147,10 @@\n     +SYNOPSIS\n     +--------\n     +[verse]\n    -+'git switch' [<options>] [--guess] <branch>\n    ++'git switch' [<options>] [--no-guess] <branch>\n     +'git switch' [<options>] --detach [<start-point>]\n    -+'git switch' [<options>] (-c|-C|--orphan) <new-branch> [<start-point>]\n    ++'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]\n    ++'git switch' [<options>] --orphan <new-branch>\n     +\n     +DESCRIPTION\n     +-----------\n    @@ -164,8 +165,8 @@\n     +\n     +Switching branches does not require a clean index and working tree\n     +(i.e. no differences compared to `HEAD`). The operation is aborted\n    -+however if the switch leads to loss of local changes, unless told\n    -+otherwise.\n    ++however if the operation leads to loss of local changes, unless told\n    ++otherwise with `--discard-changes` or `--merge`.\n     +\n     +OPTIONS\n     +-------\n    @@ -183,7 +184,7 @@\n     +\tfrom some other point.)\n     ++\n     +You can use the `@{-N}` syntax to refer to the N-th last\n    -+branch/commit switched to \"git switch\" or \"git checkout\"\n    ++branch/commit switched to using \"git switch\" or \"git checkout\"\n     +operation. You may also specify `-` which is synonymous to `@{-1}`.\n     +This is often used to switch quickly between two branches, or to undo\n     +a branch switch by mistake.\n    @@ -242,7 +243,7 @@\n     +\n     +-f::\n     +--force::\n    -+\tAn alias for `--discard-changes`.\n    ++\tAn alias for `--discard-changes` and `--ignore-in-progress`.\n     +\n     +--discard-changes::\n     +\tProceed even if the index or the working tree differs from\n    @@ -250,6 +251,11 @@\n     +\tthe switching target. This is used to throw away local\n     +\tchanges.\n     +\n    ++--ignore-in-progress::\n    ++\t`git switch` by default refuses when some operation is in\n    ++\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n    ++\toverrides this safety check and allows switching.\n    ++\n     +-m::\n     +--merge::\n     +\tIf you have local modifications to one or more files that are\n    @@ -304,10 +310,8 @@\n     +\t`branch.autoSetupMerge` configuration variable is true.\n     +\n     +--orphan <new-branch>::\n    -+\tCreate a new 'orphan' branch, named `<new-branch>`. If\n    -+\t`<start-point>` is specified, the index and working tree are\n    -+\tadjusted to match it. Otherwise both are adjusted to contain no\n    -+\ttracked files.\n    ++\tCreate a new 'orphan' branch, named `<new-branch>`. All\n    ++\ttracked files are removed.\n     +\n     +--ignore-other-worktrees::\n     +\t`git switch` refuses when the wanted ref is already\n    @@ -315,11 +319,6 @@\n     +\tthe ref out anyway. In other words, the ref can be held by\n     +\tmore than one worktree.\n     +\n    -+--ignore-in-progress::\n    -+\t`git switch` by default refuses when some operation is in\n    -+\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n    -+\toverrides this safety check and allows switching.\n    -+\n     +--recurse-submodules::\n     +--no-recurse-submodules::\n     +\tUsing `--recurse-submodules` will update the content of all initialized\n11:  c966bacfcc = 11:  676f5df0fd switch: better names for -b and -B\n12:  bdb88bf9a9 ! 12:  4e37c0659e switch: add --discard-changes\n    @@ -4,8 +4,8 @@\n     \n         --discard-changes is a better name than --force for this option since\n         it's what really happens. --force is turned to an alias for\n    -    --discard-changes. But it's meant to an alias for potentially more force\n    -    options in the future.\n    +    --discard-changes. But it's meant to be an alias for potentially more\n    +    force options in the future.\n     \n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n13:  d5fe7f4bd0 = 13:  a57208d137 switch: remove -l\n14:  3bce4c521e = 14:  3de6f95bf2 switch: stop accepting pathspec\n15:  dad0063fc4 = 15:  ad225517cd switch: reject \"do nothing\" case\n16:  41ca042917 = 16:  583cfd5cc4 switch: only allow explicit detached HEAD\n17:  a0b9f1b285 = 17:  1c5aee658d switch: add short option for --detach\n18:  3d254df104 = 18:  d942ac52e2 switch: implicit dwim, use --no-guess to disable it\n19:  c6ea203f36 = 19:  37eb152c0d switch: no worktree status unless real branch switch happens\n20:  5c4effc7fd ! 20:  50d6768afd switch: reject if some operation is in progress\n    @@ -12,6 +12,9 @@\n         that separate thing you want to work on and leave this worktree\n         alone (unless of course creating or preparing worktrees are not cheap).\n     \n    +    --force is updated to also imply --ignore-in-progress because it is\n    +    supposed to be the \"just do your things and don't bother me\" option.\n    +\n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n      +++ b/builtin/checkout.c\n    @@ -45,7 +48,7 @@\n     +\t\t\tdie(_(\"cannot switch branch while merging\"));\n     +\t\tif (state.am_in_progress)\n     +\t\t\tdie(_(\"cannot switch branch in the middle of an am session\"));\n    -+\t\tif (state.rebase_in_progress || state.rebase_in_progress)\n    ++\t\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n     +\t\t\tdie(_(\"cannot switch branch while rebasing\"));\n     +\t\tif (state.cherry_pick_in_progress)\n     +\t\t\tdie(_(\"cannot switch branch while cherry-picking\"));\n    @@ -58,6 +61,18 @@\n      \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n      \t    !opts->ignore_other_worktrees) {\n      \t\tint flag;\n    +@@\n    + \t\topts->merge = 1; /* implied */\n    + \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n    + \t}\n    +-\tif (opts->force)\n    ++\tif (opts->force) {\n    + \t\topts->discard_changes = 1;\n    ++\t\topts->can_switch_when_in_progress = 1;\n    ++\t}\n    + \n    + \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n    + \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n     @@\n      \topts.only_merge_on_switching_branches = 0;\n      \topts.accept_pathspec = 1;\n21:  a4afe6a999 ! 21:  af8bb710c8 switch: --orphan defaults to empty tree as HEAD\n    @@ -1,15 +1,16 @@\n     Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n     \n    -    switch: --orphan defaults to empty tree as HEAD\n    +    switch: make --orphan switch to an empty tree\n     \n         Switching and creating branches always involves knowing the\n         <start-point> to begin the new branch from. Sometimes, people want to\n         create a new branch that does not have any commits yet; --orphan is a\n         flag to allow that.\n     \n    -    If <start-point> is not specified, instead of leaving index/worktree\n    -    unchanged, default to \"empty tree\" starting point, allowing a clean\n    -    start.\n    +    --orphan overrides the default of HEAD for <start-point> instead causing\n    +    us to start from an empty history with all tracked files removed from\n    +    the index and working tree. The use of --orphan is incompatible with\n    +    specifying a <start-point>.\n     \n         A note on the implementation. An alternative is just create a dummy\n         commit in-core with empty tree and switch to it. But there's a chance\n    @@ -23,7 +24,7 @@\n      \tint switch_branch_doing_nothing_is_ok;\n      \tint only_merge_on_switching_branches;\n      \tint can_switch_when_in_progress;\n    -+\tint orphan_default_empty_tree;\n    ++\tint orphan_from_empty_tree;\n      \n      \tconst char *new_branch;\n      \tconst char *new_branch_force;\n    @@ -38,9 +39,11 @@\n      \t\treturn error(_(\"index file corrupt\"));\n      \n      \tresolve_undo_clear();\n    -+\tif (opts->orphan_default_empty_tree && !new_branch_info->commit)\n    ++\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n    ++\t\tif (new_branch_info->commit)\n    ++\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n     +\t\tnew_tree = parse_tree_indirect(the_hash_algo->empty_tree);\n    -+\telse\n    ++\t} else\n     +\t\tnew_tree = get_commit_tree(new_branch_info->commit);\n      \tif (opts->discard_changes) {\n     -\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n    @@ -106,23 +109,34 @@\n      \tif (prepare_revision_walk(&revs))\n      \t\tdie(_(\"internal error in revision walk\"));\n     @@\n    + \tif (old_branch_info.path)\n    + \t\tskip_prefix(old_branch_info.path, \"refs/heads/\", &old_branch_info.name);\n      \n    - \t\tif (opts->only_merge_on_switching_branches)\n    - \t\t\tdo_merge = 0;\n    ++\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n    ++\t\tif (new_branch_info->name)\n    ++\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n    ++\t\tnew_branch_info->commit = NULL;\n    ++\t\tnew_branch_info->name = \"(empty)\";\n    ++\t\tdo_merge = 1;\n    ++\t}\n     +\n    -+\t\tif (opts->new_orphan_branch && opts->orphan_default_empty_tree) {\n    -+\t\t\tnew_branch_info->commit = NULL;\n    -+\t\t\tnew_branch_info->name = \"(empty)\";\n    -+\t\t\tdo_merge = 1;\n    -+\t\t}\n    - \t}\n    - \n    - \tif (do_merge) {\n    + \tif (!new_branch_info->name) {\n    + \t\tnew_branch_info->name = \"HEAD\";\n    + \t\tnew_branch_info->commit = old_branch_info.commit;\n    +@@\n    + \tif (opts->new_orphan_branch) {\n    + \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n    + \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--orphan\", \"-t\");\n    ++\t\tif (opts->orphan_from_empty_tree && new_branch_info->name)\n    ++\t\t\tdie(_(\"'%s' cannot take <start-point>\"), \"--orphan\");\n    + \t} else if (opts->force_detach) {\n    + \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n    + \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n     @@\n      \topts.accept_pathspec = 1;\n      \topts.implicit_detach = 1;\n      \topts.can_switch_when_in_progress = 1;\n    -+\topts.orphan_default_empty_tree = 0;\n    ++\topts.orphan_from_empty_tree = 0;\n      \n      \toptions = parse_options_dup(checkout_options);\n      \toptions = add_common_options(&opts, options);\n    @@ -130,7 +144,7 @@\n      \topts.only_merge_on_switching_branches = 1;\n      \topts.implicit_detach = 0;\n      \topts.can_switch_when_in_progress = 0;\n    -+\topts.orphan_default_empty_tree = 1;\n    ++\topts.orphan_from_empty_tree = 1;\n      \n      \toptions = parse_options_dup(switch_options);\n      \toptions = add_common_options(&opts, options);\n22:  6cca78f835 ! 22:  4a293a3d53 t: add tests for switch\n    @@ -67,6 +67,7 @@\n     +\n     +test_expect_success 'new orphan branch from empty' '\n     +\ttest_when_finished git switch master &&\n    ++\ttest_must_fail git switch --orphan new-orphan HEAD &&\n     +\tgit switch --orphan new-orphan &&\n     +\ttest_commit orphan &&\n     +\tgit cat-file commit refs/heads/new-orphan >commit &&\n23:  e5e6f9d6f1 = 23:  b10290b378 completion: support switch\n24:  807e8bc50b = 24:  67c5175e5f doc: promote \"git switch\"\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372095","messageId":"20190321131655.15249-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 01/26] git-checkout.txt: spell out --no-option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:30Z","receivedAt":"2019-03-21T13:17:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's easier to search for and also less cryptic.\n---\n Documentation/git-checkout.txt | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f179b43732..99c8c0dc0f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -118,7 +118,8 @@ OPTIONS\n --quiet::\n \tQuiet, suppress feedback messages.\n \n---[no-]progress::\n+--progress::\n+--no-progress::\n \tProgress status is reported on the standard error stream\n \tby default when it is attached to a terminal, unless `--quiet`\n \tis specified. This flag enables progress reporting even if not\n@@ -262,7 +263,7 @@ edits from your current working tree. See the ``Interactive Mode''\n section of linkgit:git-add[1] to learn how to operate the `--patch` mode.\n +\n Note that this option uses the no overlay mode by default (see also\n-`--[no-]overlay`), and currently doesn't support overlay mode.\n+`--overlay`), and currently doesn't support overlay mode.\n \n --ignore-other-worktrees::\n \t`git checkout` refuses when the wanted ref is already checked\n@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n---[no-]recurse-submodules::\n+--recurse-submodules::\n+--no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also\n \tDo not attempt to create a branch if a remote tracking branch\n \tof the same name exists.\n \n---[no-]overlay::\n+--overlay::\n+--no-overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372096","messageId":"20190321131655.15249-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 02/26] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:31Z","receivedAt":"2019-03-21T13:17:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 99c8c0dc0f..28817cfa41 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372097","messageId":"20190321131655.15249-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 03/26] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:32Z","receivedAt":"2019-03-21T13:17:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n---\n Documentation/git-checkout.txt | 6 ++++++\n Documentation/git-merge.txt    | 5 +++++\n 2 files changed, 11 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 28817cfa41..5280d1f9ed 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --recurse-submodules::\n --no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..6a9163d8fe 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,11 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372098","messageId":"20190321131655.15249-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 04/26] git-checkout.txt: fix monospace typeset","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:33Z","receivedAt":"2019-03-21T13:17:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Add backticks where we have none, replace single quotes with backticks\nand replace double-quotes. Drop double-quotes from nested constructions\nsuch as `\"@{-1}\"`.\n\nHelped-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\n---\n Documentation/git-checkout.txt | 162 ++++++++++++++++-----------------\n 1 file changed, 81 insertions(+), 81 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 5280d1f9ed..bf90966c95 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current\n branch.\n \n 'git checkout' [<branch>]::\n-\tTo prepare for working on <branch>, switch to it by updating\n+\tTo prepare for working on `<branch>`, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n-\tHEAD at the branch. Local modifications to the files in the\n+\t`HEAD` at the branch. Local modifications to the files in the\n \tworking tree are kept, so that they can be committed to the\n-\t<branch>.\n+\t`<branch>`.\n +\n-If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n+If `<branch>` is not found but there does exist a tracking branch in\n+exactly one remote (call it `<remote>`) with a matching name, treat as\n equivalent to\n +\n ------------\n@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the\n 'origin' remote. See also `checkout.defaultRemote` in\n linkgit:git-config[1].\n +\n-You could omit <branch>, in which case the command degenerates to\n+You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n if exists, for the current branch.\n@@ -61,7 +61,7 @@ if exists, for the current branch.\n \t`--track` without `-b` implies branch creation; see the\n \tdescription of `--track` below.\n +\n-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it\n+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it\n is reset. This is the transactional equivalent of\n +\n ------------\n@@ -75,25 +75,25 @@ successful.\n 'git checkout' --detach [<branch>]::\n 'git checkout' [--detach] <commit>::\n \n-\tPrepare to work on top of <commit>, by detaching HEAD at it\n+\tPrepare to work on top of `<commit>`, by detaching `HEAD` at it\n \t(see \"DETACHED HEAD\" section), and updating the index and the\n \tfiles in the working tree.  Local modifications to the files\n \tin the working tree are kept, so that the resulting working\n \ttree will be the state recorded in the commit plus the local\n \tmodifications.\n +\n-When the <commit> argument is a branch name, the `--detach` option can\n-be used to detach HEAD at the tip of the branch (`git checkout\n-<branch>` would check out that branch without detaching HEAD).\n+When the `<commit>` argument is a branch name, the `--detach` option can\n+be used to detach `HEAD` at the tip of the branch (`git checkout\n+<branch>` would check out that branch without detaching `HEAD`).\n +\n-Omitting <branch> detaches HEAD at the tip of the current branch.\n+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.\n \n 'git checkout' [<tree-ish>] [--] <pathspec>...::\n \n \tOverwrite paths in the working tree by replacing with the\n-\tcontents in the index or in the <tree-ish> (most often a\n-\tcommit).  When a <tree-ish> is given, the paths that\n-\tmatch the <pathspec> are updated both in the index and in\n+\tcontents in the index or in the `<tree-ish>` (most often a\n+\tcommit).  When a `<tree-ish>` is given, the paths that\n+\tmatch the `<pathspec>` are updated both in the index and in\n \tthe working tree.\n +\n The index may contain unmerged entries because of a previous failed merge.\n@@ -128,7 +128,7 @@ OPTIONS\n -f::\n --force::\n \tWhen switching branches, proceed even if the index or the\n-\tworking tree differs from HEAD.  This is used to throw away\n+\tworking tree differs from `HEAD`.  This is used to throw away\n \tlocal changes.\n +\n When checking out paths from the index, do not fail upon unmerged\n@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. \"one contributor's work on top\n of it\").\n \n -b <new_branch>::\n-\tCreate a new branch named <new_branch> and start it at\n-\t<start_point>; see linkgit:git-branch[1] for details.\n+\tCreate a new branch named `<new_branch>` and start it at\n+\t`<start_point>`; see linkgit:git-branch[1] for details.\n \n -B <new_branch>::\n-\tCreates the branch <new_branch> and start it at <start_point>;\n-\tif it already exists, then reset it to <start_point>. This is\n+\tCreates the branch `<new_branch>` and start it at `<start_point>`;\n+\tif it already exists, then reset it to `<start_point>`. This is\n \tequivalent to running \"git branch\" with \"-f\"; see\n \tlinkgit:git-branch[1] for details.\n \n@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be\n derived from the remote-tracking branch, by looking at the local part of\n the refspec configured for the corresponding remote, and then stripping\n the initial part up to the \"*\".\n-This would tell us to use \"hack\" as the local branch when branching\n-off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n-\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n+This would tell us to use `hack` as the local branch when branching\n+off of `origin/hack` (or `remotes/origin/hack`, or even\n+`refs/remotes/origin/hack`).  If the given name has no slash, or the above\n guessing results in an empty name, the guessing is aborted.  You can\n explicitly give a name with `-b` in such a case.\n \n --no-track::\n \tDo not set up \"upstream\" configuration, even if the\n-\tbranch.autoSetupMerge configuration variable is true.\n+\t`branch.autoSetupMerge` configuration variable is true.\n \n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.\n --detach::\n \tRather than checking out a branch to work on it, check out a\n \tcommit for inspection and discardable experiments.\n-\tThis is the default behavior of \"git checkout <commit>\" when\n-\t<commit> is not a branch name.  See the \"DETACHED HEAD\" section\n+\tThis is the default behavior of `git checkout <commit>` when\n+\t`<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n \tbelow for details.\n \n --orphan <new_branch>::\n-\tCreate a new 'orphan' branch, named <new_branch>, started from\n-\t<start_point> and switch to it.  The first commit made on this\n+\tCreate a new 'orphan' branch, named `<new_branch>`, started from\n+\t`<start_point>` and switch to it.  The first commit made on this\n \tnew branch will have no parents and it will be the root of a new\n \thistory totally disconnected from all the other branches and\n \tcommits.\n +\n The index and the working tree are adjusted as if you had previously run\n-\"git checkout <start_point>\".  This allows you to start a new history\n-that records a set of paths similar to <start_point> by easily running\n-\"git commit -a\" to make the root commit.\n+`git checkout <start_point>`.  This allows you to start a new history\n+that records a set of paths similar to `<start_point>` by easily running\n+`git commit -a` to make the root commit.\n +\n This can be useful when you want to publish the tree from a commit\n without exposing its full history. You might want to do this to publish\n@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of\n code.\n +\n If you want to start a disconnected history that records a set of paths\n-that is totally different from the one of <start_point>, then you should\n+that is totally different from the one of `<start_point>`, then you should\n clear the index and the working tree right after creating the orphan\n-branch by running \"git rm -rf .\" from the top level of the working tree.\n+branch by running `git rm -rf .` from the top level of the working tree.\n Afterwards you will be ready to prepare your new files, repopulating the\n working tree, by copying them from elsewhere, extracting a tarball, etc.\n \n --ignore-skip-worktree-bits::\n \tIn sparse checkout mode, `git checkout -- <paths>` would\n-\tupdate only entries matched by <paths> and sparse patterns\n-\tin $GIT_DIR/info/sparse-checkout. This option ignores\n-\tthe sparse patterns and adds back any files in <paths>.\n+\tupdate only entries matched by `<paths>` and sparse patterns\n+\tin `$GIT_DIR/info/sparse-checkout`. This option ignores\n+\tthe sparse patterns and adds back any files in `<paths>`.\n \n -m::\n --merge::\n@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n \n --conflict=<style>::\n-\tThe same as --merge option above, but changes the way the\n+\tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n-\tmerge.conflictStyle configuration variable.  Possible values are\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n \t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n \t\"merge\" style, shows the original contents).\n \n -p::\n --patch::\n \tInteractively select hunks in the difference between the\n-\t<tree-ish> (or the index, if unspecified) and the working\n+\t`<tree-ish>` (or the index, if unspecified) and the working\n \ttree.  The chosen hunks are then applied in reverse to the\n-\tworking tree (and if a <tree-ish> was specified, the index).\n+\tworking tree (and if a `<tree-ish>` was specified, the index).\n +\n This means that you can use `git checkout -p` to selectively discard\n edits from your current working tree. See the ``Interactive Mode''\n@@ -279,13 +279,13 @@ Note that this option uses the no overlay mode by default (see also\n \n --recurse-submodules::\n --no-recurse-submodules::\n-\tUsing --recurse-submodules will update the content of all initialized\n+\tUsing `--recurse-submodules` will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n \tis used, the work trees of submodules will not be updated.\n-\tJust like linkgit:git-submodule[1], this will detach the\n-\tsubmodules HEAD.\n+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodule.\n \n --no-guess::\n \tDo not attempt to create a branch if a remote tracking branch\n@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-\tworking tree, but not in <tree-ish> are removed, to make them\n-\tmatch <tree-ish> exactly.\n+\tworking tree, but not in `<tree-ish>` are removed, to make them\n+\tmatch `<tree-ish>` exactly.\n \n <branch>::\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n \twhen prepended with \"refs/heads/\", is a valid ref), then that\n \tbranch is checked out. Otherwise, if it refers to a valid\n-\tcommit, your HEAD becomes \"detached\" and you are no longer on\n+\tcommit, your `HEAD` becomes \"detached\" and you are no longer on\n \tany branch (see below for details).\n +\n-You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+You can use the `@{-N}` syntax to refer to the N-th last\n branch/commit checked out using \"git checkout\" operation. You may\n-also specify `-` which is synonymous to `\"@{-1}\"`.\n+also specify `-` which is synonymous to `@{-1}`.\n +\n-As a special case, you may use `\"A...B\"` as a shortcut for the\n+As a special case, you may use `A...B` as a shortcut for the\n merge base of `A` and `B` if there is exactly one merge base. You can\n leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n <start_point>::\n \tThe name of a commit at which to start the new branch; see\n-\tlinkgit:git-branch[1] for details. Defaults to HEAD.\n+\tlinkgit:git-branch[1] for details. Defaults to `HEAD`.\n \n <tree-ish>::\n \tTree to checkout from (when paths are given). If not specified,\n@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n DETACHED HEAD\n -------------\n-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each\n branch refers to a specific commit. Let's look at a repo with three\n-commits, one of them tagged, and with branch 'master' checked out:\n+commits, one of them tagged, and with branch `master` checked out:\n \n ------------\n            HEAD (refers to branch 'master')\n@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')\n ------------\n \n When a commit is created in this state, the branch is updated to refer to\n-the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n-parent is commit 'c', and then updates branch 'master' to refer to new\n-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n-to commit 'd':\n+the new commit. Specifically, 'git commit' creates a new commit `d`, whose\n+parent is commit `c`, and then updates branch `master` to refer to new\n+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers\n+to commit `d`:\n \n ------------\n $ edit; git add; git commit\n@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n It is sometimes useful to be able to checkout a commit that is not at\n the tip of any named branch, or even to create a new commit that is not\n referenced by a named branch. Let's look at what happens when we\n-checkout commit 'b' (here we show two ways this may be done):\n+checkout commit `b` (here we show two ways this may be done):\n \n ------------\n $ git checkout v2.0  # or\n@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-Notice that regardless of which checkout command we use, HEAD now refers\n-directly to commit 'b'. This is known as being in detached HEAD state.\n-It means simply that HEAD refers to a specific commit, as opposed to\n+Notice that regardless of which checkout command we use, `HEAD` now refers\n+directly to commit `b`. This is known as being in detached `HEAD` state.\n+It means simply that `HEAD` refers to a specific commit, as opposed to\n referring to a named branch. Let's see what happens when we create a commit:\n \n ------------\n@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-There is now a new commit 'e', but it is referenced only by HEAD. We can\n+There is now a new commit `e`, but it is referenced only by `HEAD`. We can\n of course add yet another commit in this state:\n \n ------------\n@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n In fact, we can perform all the normal Git operations. But, let's look\n-at what happens when we then checkout master:\n+at what happens when we then checkout `master`:\n \n ------------\n $ git checkout master\n@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n It is important to realize that at this point nothing refers to commit\n-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted\n by the routine Git garbage collection process, unless we create a reference\n-before that happens. If we have not yet moved away from commit 'f',\n+before that happens. If we have not yet moved away from commit `f`,\n any of these will create a reference to it:\n \n ------------\n@@ -442,19 +442,19 @@ $ git branch foo        <2>\n $ git tag foo           <3>\n ------------\n \n-<1> creates a new branch 'foo', which refers to commit 'f', and then\n-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n-    be in detached HEAD state after this command.\n+<1> creates a new branch `foo`, which refers to commit `f`, and then\n+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer\n+    be in detached `HEAD` state after this command.\n \n-<2> similarly creates a new branch 'foo', which refers to commit 'f',\n-    but leaves HEAD detached.\n+<2> similarly creates a new branch `foo`, which refers to commit `f`,\n+    but leaves `HEAD` detached.\n \n-<3> creates a new tag 'foo', which refers to commit 'f',\n-    leaving HEAD detached.\n+<3> creates a new tag `foo`, which refers to commit `f`,\n+    leaving `HEAD` detached.\n \n-If we have moved away from commit 'f', then we must first recover its object\n+If we have moved away from commit `f`, then we must first recover its object\n name (typically by using git reflog), and then we can create a reference to\n-it. For example, to see the last two commits to which HEAD referred, we\n+it. For example, to see the last two commits to which `HEAD` referred, we\n can use either of these commands:\n \n ------------\n@@ -465,12 +465,12 @@ $ git log -g -2 HEAD\n ARGUMENT DISAMBIGUATION\n -----------------------\n \n-When there is only one argument given and it is not `--` (e.g. \"git\n-checkout abc\"), and when the argument is both a valid `<tree-ish>`\n-(e.g. a branch \"abc\" exists) and a valid `<pathspec>` (e.g. a file\n+When there is only one argument given and it is not `--` (e.g. `git\n+checkout abc`), and when the argument is both a valid `<tree-ish>`\n+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file\n or a directory whose name is \"abc\" exists), Git would usually ask\n you to disambiguate.  Because checking out a branch is so common an\n-operation, however, \"git checkout abc\" takes \"abc\" as a `<tree-ish>`\n+operation, however, `git checkout abc` takes \"abc\" as a `<tree-ish>`\n in such a situation.  Use `git checkout -- <pathspec>` if you want\n to checkout these paths out of the index.\n \n@@ -478,7 +478,7 @@ EXAMPLES\n --------\n \n . The following sequence checks out the `master` branch, reverts\n-  the `Makefile` to two revisions back, deletes hello.c by\n+  the `Makefile` to two revisions back, deletes `hello.c` by\n   mistake, and gets it back from the index.\n +\n ------------\n@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>\n +\n <1> switch branch\n <2> take a file out of another commit\n-<3> restore hello.c from the index\n+<3> restore `hello.c` from the index\n +\n If you want to check out _all_ C source files out of the index,\n you can say\n@@ -519,7 +519,7 @@ $ git checkout -- hello.c\n $ git checkout mytopic\n ------------\n +\n-However, your \"wrong\" branch and correct \"mytopic\" branch may\n+However, your \"wrong\" branch and correct `mytopic` branch may\n differ in files that you have modified locally, in which case\n the above checkout would fail like this:\n +\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372099","messageId":"20190321131655.15249-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 05/26] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:34Z","receivedAt":"2019-03-21T13:17:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372100","messageId":"20190321131655.15249-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 06/26] checkout: advice how to get out of detached HEAD mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:35Z","receivedAt":"2019-03-21T13:17:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Detached HEAD mode is considered dangerous and confusing for newcomers\nand we print a big block of warning how to move forward. But we should\nalso suggest the user the way to get out of it if they get into detached\nHEAD by mistake.\n\nWhile at there, I also suggest how to turn the advice off. This is\nanother thing I find annoying with advices and should be dealt with in a\nmore generic way. But that may require some refactoring in advice.c\nfirst.\n---\n advice.c                   | 17 +++++++++++++----\n t/t2020-checkout-detach.sh | 12 ++++++++++++\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..b224825637 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: checking out '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"\\n\"\n+\t\"Or undo this checkout with:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -\\n\"\n+\t\"\\n\"\n+\t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..743c7248a2 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit three\n \tEOF\n \n@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit... three\n \tEOF\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372101","messageId":"20190321131655.15249-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 07/26] checkout: keep most #include sorted","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:36Z","receivedAt":"2019-03-21T13:18:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The include list becomes very long and frankly a bit unorganized. With\nthe exception of builtin.h, cache.h or git-compat-util.h which have to\ncome first, keep the rest sorted.\n---\n builtin/checkout.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e6037b296..f7801db2fb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1,30 +1,30 @@\n #define USE_THE_INDEX_COMPATIBILITY_MACROS\n #include \"builtin.h\"\n-#include \"config.h\"\n+#include \"advice.h\"\n+#include \"blob.h\"\n+#include \"branch.h\"\n+#include \"cache-tree.h\"\n #include \"checkout.h\"\n+#include \"commit.h\"\n+#include \"config.h\"\n+#include \"diff.h\"\n+#include \"dir.h\"\n+#include \"ll-merge.h\"\n #include \"lockfile.h\"\n+#include \"merge-recursive.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n-#include \"object-store.h\"\n-#include \"commit.h\"\n+#include \"remote.h\"\n+#include \"resolve-undo.h\"\n+#include \"revision.h\"\n+#include \"run-command.h\"\n+#include \"submodule.h\"\n+#include \"submodule-config.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n-#include \"dir.h\"\n-#include \"run-command.h\"\n-#include \"merge-recursive.h\"\n-#include \"branch.h\"\n-#include \"diff.h\"\n-#include \"revision.h\"\n-#include \"remote.h\"\n-#include \"blob.h\"\n #include \"xdiff-interface.h\"\n-#include \"ll-merge.h\"\n-#include \"resolve-undo.h\"\n-#include \"submodule-config.h\"\n-#include \"submodule.h\"\n-#include \"advice.h\"\n \n static int checkout_optimize_new_branch;\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372102","messageId":"20190321131655.15249-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 08/26] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:37Z","receivedAt":"2019-03-21T13:18:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f7801db2fb..1ce0446bc6 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372103","messageId":"20190321131655.15249-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 09/26] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:38Z","receivedAt":"2019-03-21T13:18:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1ce0446bc6..963f10b05b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372104","messageId":"20190321131655.15249-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 10/26] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:39Z","receivedAt":"2019-03-21T13:18:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 963f10b05b..b6fc1fc69f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -47,6 +47,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -58,6 +60,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372105","messageId":"20190321131655.15249-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 12/26] checkout: split part of it to new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:41Z","receivedAt":"2019-03-21T13:18:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and restore. The good\nold \"git checkout\" command is still here and will be until all (or most\nof users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n---\n .gitignore                         |   1 +\n Documentation/config/advice.txt    |  13 +-\n Documentation/config/branch.txt    |   4 +-\n Documentation/config/checkout.txt  |   9 +-\n Documentation/config/diff.txt      |   3 +-\n Documentation/git-checkout.txt     |   4 +\n Documentation/git-switch.txt (new) | 276 +++++++++++++++++++++++++++++\n Documentation/gitattributes.txt    |   3 +-\n Documentation/githooks.txt         |   8 +-\n Makefile                           |   1 +\n builtin.h                          |   1 +\n builtin/checkout.c                 |  60 +++++--\n command-list.txt                   |   1 +\n git.c                              |   1 +\n 14 files changed, 358 insertions(+), 27 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 7374587f9d..c687b92b1c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex c4118fa196..d6872ffa83 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex bf90966c95..c7192bdefe 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -560,6 +560,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..da2cbaf890\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,276 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--no-guess] <branch>\n+'git switch' [<options>] --detach [<start-point>]\n+'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]\n+'git switch' [<options>] --orphan <new-branch>\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch. The working tree and the index are\n+updated to match the branch. All new commits will be added to the tip\n+of this branch.\n+\n+Optionally a new branch could be created with either `-c`, `-C`,\n+automatically from a remote branch of same name (see `--guess`), or\n+detach the working tree from any branch with `--detach`, along with\n+switching.\n+\n+Switching branches does not require a clean index and working tree\n+(i.e. no differences compared to `HEAD`). The operation is aborted\n+however if the operation leads to loss of local changes, unless told\n+otherwise with `--discard-changes` or `--merge`.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new-branch>::\n+\tName for the new branch.\n+\n+<start-point>::\n+\tThe starting point for the new branch. Specifying a\n+\t`<start-point>` allows you to create a branch based on some\n+\tother point in history than where HEAD currently points. (Or,\n+\tin the case of `--detach`, allows you to inspect and detach\n+\tfrom some other point.)\n++\n+You can use the `@{-N}` syntax to refer to the N-th last\n+branch/commit switched to using \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `@{-1}`.\n+This is often used to switch quickly between two branches, or to undo\n+a branch switch by mistake.\n++\n+As a special case, you may use `A...B` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new-branch>::\n+--create <new-branch>::\n+\tCreate a new branch named `<new-branch>` starting at\n+\t`<start-point>` before switching to the branch. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-C <new-branch>::\n+--force-create <new-branch>::\n+\tSimilar to `--create` except that if `<new-branch>` already\n+\texists, it will be reset to `<start-point>`. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch -f <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n++\n+`--guess` is the default behavior. Use `--no-guess` to disable it.\n+\n+-f::\n+--force::\n+\tAn alias for `--discard-changes` and `--ignore-in-progress`.\n+\n+--discard-changes::\n+\tProceed even if the index or the working tree differs from\n+\t`HEAD`. Both the index and working tree are restored to match\n+\tthe switching target. This is used to throw away local\n+\tchanges.\n+\n+--ignore-in-progress::\n+\t`git switch` by default refuses when some operation is in\n+\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n+\toverrides this safety check and allows switching.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See `--track` in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use `hack` as\n+the local branch when branching off of `origin/hack` (or\n+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\t`branch.autoSetupMerge` configuration variable is true.\n+\n+--orphan <new-branch>::\n+\tCreate a new 'orphan' branch, named `<new-branch>`. All\n+\ttracked files are removed.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--recurse-submodules::\n+--no-recurse-submodules::\n+\tUsing `--recurse-submodules` will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodule.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name:\n+\n+------------\n+$ git switch -g new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit `HEAD~3` for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1],\n+linkgit:git-branch[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 9b41f81c06..cd0f9fa507 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 5bf653c111..8ff72f0613 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -406,7 +407,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 148668368b..8e91db73ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cf1d70511e..dd3c200cb5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 2014aab6b8..39582cf511 100644\n--- a/git.c\n+++ b/git.c\n@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372106","messageId":"20190321131655.15249-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 13/26] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:42Z","receivedAt":"2019-03-21T13:18:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout. Move -b/-B\nto cmd_checkout() and new -c/-C with the same functionality in\ncmd_switch_branch()\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex dd3c200cb5..400a0d5620 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372107","messageId":"20190321131655.15249-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 14/26] switch: add --discard-changes","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:43Z","receivedAt":"2019-03-21T13:18:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"--discard-changes is a better name than --force for this option since\nit's what really happens. --force is turned to an alias for\n--discard-changes. But it's meant to be an alias for potentially more\nforce options in the future.\n---\n builtin/checkout.c | 12 ++++++++++--\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 400a0d5620..bd94b04303 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint discard_changes;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n-\tif (opts->force) {\n+\tif (opts->discard_changes) {\n \t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n \t\t\t\t opts, 1, writeout_error);\n \t\tif (ret)\n@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->force && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \tif (opts->force && opts->merge)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-f\", \"-m\");\n \n+\tif (opts->discard_changes && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--discard-changes\", \"--merge\");\n+\n \tif (opts->force_detach && opts->new_branch)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"),\n \t\t    \"--detach\", \"-b/-B/--orphan\");\n@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n+\tif (opts->force)\n+\t\topts->discard_changes = 1;\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n+\t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372108","messageId":"20190321131655.15249-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 15/26] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:44Z","receivedAt":"2019-03-21T13:19:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex bd94b04303..85fc60f5f2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372109","messageId":"20190321131655.15249-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 16/26] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:45Z","receivedAt":"2019-03-21T13:19:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 85fc60f5f2..2c4cab6406 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -54,6 +54,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372110","messageId":"20190321131655.15249-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 11/26] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:40Z","receivedAt":"2019-03-21T13:19:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  1 +\n 3 files changed, 77 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b6fc1fc69f..cf1d70511e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 2733393546..caaeed896f 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..9a90c332a5 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n+struct option *parse_options_dup(const struct option *a);\n struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372111","messageId":"20190321131655.15249-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 17/26] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:46Z","receivedAt":"2019-03-21T13:19:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2c4cab6406..73b7373414 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -55,6 +55,7 @@ struct checkout_opts {\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"missing branch or commit argument\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372112","messageId":"20190321131655.15249-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 18/26] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:47Z","receivedAt":"2019-03-21T13:19:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n---\n builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++\n 1 file changed, 34 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 73b7373414..d72e28aabb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -45,6 +45,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)\n \treturn status;\n }\n \n+static void die_expecting_a_branch(const struct branch_info *branch_info)\n+{\n+\tstruct object_id oid;\n+\tchar *to_free;\n+\n+\tif (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {\n+\t\tconst char *ref = to_free;\n+\n+\t\tif (skip_prefix(ref, \"refs/tags/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got tag '%s'\"), ref);\n+\t\tif (skip_prefix(ref, \"refs/remotes/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got remote branch '%s'\"), ref);\n+\t\tdie(_(\"a branch is expected, got '%s'\"), ref);\n+\t}\n+\tif (branch_info->commit)\n+\t\tdie(_(\"a branch is expected, got commit '%s'\"), branch_info->name);\n+\t/*\n+\t * This case should never happen because we already die() on\n+\t * non-commit, but just in case.\n+\t */\n+\tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n+}\n+\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"missing branch or commit argument\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie_expecting_a_branch(new_branch_info);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372113","messageId":"20190321131655.15249-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 19/26] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:48Z","receivedAt":"2019-03-21T13:19:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d72e28aabb..a14133b474 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372114","messageId":"20190321131655.15249-21-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 20/26] switch: implicit dwim, use --no-guess to disable it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:49Z","receivedAt":"2019-03-21T13:19:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is already the default in git-checkout. The real change in here is\njust minor cleanup. The main excuse is to explain why dwim is kept default.\n\nContrary to detach mode that is easy to get into and confusing to get\nback out. Automatically creating a tracking branch often does not kick\nin as often (you would need a branch of the same name on a remote). And\nsince the branch creation is reported clearly, the user should be able\nto undo/delete it if it's unwanted.\n---\n Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++-------\n 2 files changed, 31 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex c7192bdefe..58f18a0842 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t`<branch>`.\n +\n If `<branch>` is not found but there does exist a tracking branch in\n-exactly one remote (call it `<remote>`) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it `<remote>`) with a matching name and\n+`--no-guess` is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \t`branch.autoSetupMerge` configuration variable is true.\n \n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n \tsubmodule.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --overlay::\n --no-overlay::\n \tIn the default overlay mode, `git checkout` never\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a14133b474..ee74379038 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,7 +53,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n \t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372115","messageId":"20190321131655.15249-22-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 21/26] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:50Z","receivedAt":"2019-03-21T13:19:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 134 ++----------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 8 insertions(+), 148 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex d6872ffa83..6b646813ab 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n-\n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ee74379038..2aeaac092d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -26,8 +26,6 @@\n #include \"unpack-trees.h\"\n #include \"xdiff-interface.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -57,11 +55,7 @@ struct checkout_opts {\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\t /*\n-\t  * Do the merge if this is the initial checkout. We cannot use\n-\t  * is_cache_unborn() here because the index hasn't been loaded yet\n-\t  * so cache_nr and timestamp.sec are always zero.\n-\t  */\n-\tif (!file_exists(get_index_file()))\n-\t\treturn 0;\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n \n \ttrace2_cmd_mode(\"branch\");\n \n@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,\n \n static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"diff.ignoresubmodules\")) {\n \t\tstruct checkout_opts *opts = cb;\n \t\thandle_ignore_submodules_arg(&opts->diff_options, value);\n@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372116","messageId":"20190321131655.15249-23-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 22/26] switch: reject if some operation is in progress","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:51Z","receivedAt":"2019-03-21T13:19:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Unless you know what you're doing, switching to another branch to do\nsomething then switching back could be confusing. Worse, you may even\nforget that you're in the middle of something. By the time you realize,\nyou may have done a ton of work and it gets harder to go back.\n\nThe new option --ignore-in-progress is added to override this check.\nHowever it's probably just safer to create a separate worktree for\nthat separate thing you want to work on and leave this worktree\nalone (unless of course creating or preparing worktrees are not cheap).\n\n--force is updated to also imply --ignore-in-progress because it is\nsupposed to be the \"just do your things and don't bother me\" option.\n---\n builtin/checkout.c | 30 +++++++++++++++++++++++++++++-\n 1 file changed, 29 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2aeaac092d..407ed8be94 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -24,6 +24,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\n+#include \"wt-status.h\"\n #include \"xdiff-interface.h\"\n \n static const char * const checkout_usage[] = {\n@@ -56,6 +57,7 @@ struct checkout_opts {\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n+\tint can_switch_when_in_progress;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1257,6 +1259,26 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !new_branch_info->path)\n \t\tdie_expecting_a_branch(new_branch_info);\n \n+\tif (!opts->can_switch_when_in_progress) {\n+\t\tstruct wt_status_state state;\n+\n+\t\tmemset(&state, 0, sizeof(state));\n+\t\twt_status_get_state(the_repository, &state, 0);\n+\n+\t\tif (state.merge_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while merging\"));\n+\t\tif (state.am_in_progress)\n+\t\t\tdie(_(\"cannot switch branch in the middle of an am session\"));\n+\t\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while rebasing\"));\n+\t\tif (state.cherry_pick_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while cherry-picking\"));\n+\t\tif (state.revert_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while reverting\"));\n+\t\tif (state.bisect_in_progress)\n+\t\t\tdie(_(\"cannot switch branch while bisecting\"));\n+\t}\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1371,8 +1393,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n-\tif (opts->force)\n+\tif (opts->force) {\n \t\topts->discard_changes = 1;\n+\t\topts->can_switch_when_in_progress = 1;\n+\t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1514,6 +1538,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n+\topts.can_switch_when_in_progress = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1539,6 +1564,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n \t\t\t N_(\"throw away local modifications\")),\n+\t\tOPT_BOOL(0, \"ignore-in-progress\", &opts.can_switch_when_in_progress,\n+\t\t\t N_(\"allow to switch branch when some operation is still in progress\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n@@ -1549,6 +1576,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n+\topts.can_switch_when_in_progress = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372117","messageId":"20190321131655.15249-24-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 23/26] switch: make --orphan switch to an empty tree","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:52Z","receivedAt":"2019-03-21T13:19:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Switching and creating branches always involves knowing the\n<start-point> to begin the new branch from. Sometimes, people want to\ncreate a new branch that does not have any commits yet; --orphan is a\nflag to allow that.\n\n--orphan overrides the default of HEAD for <start-point> instead causing\nus to start from an empty history with all tracked files removed from\nthe index and working tree. The use of --orphan is incompatible with\nspecifying a <start-point>.\n\nA note on the implementation. An alternative is just create a dummy\ncommit in-core with empty tree and switch to it. But there's a chance\nthe commit's SHA-1 may end up somewhere permanent like reflog. It's best\nto make sure \"commit\" pointer is NULL to avoid it.\n---\n builtin/checkout.c | 39 +++++++++++++++++++++++++++++++--------\n 1 file changed, 31 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 407ed8be94..c7f4b5e3c0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -58,6 +58,7 @@ struct checkout_opts {\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n \tint can_switch_when_in_progress;\n+\tint orphan_from_empty_tree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -568,15 +569,21 @@ static int merge_working_tree(const struct checkout_opts *opts,\n {\n \tint ret;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tstruct tree *new_tree;\n \n \thold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(NULL) < 0)\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n+\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n+\t\tif (new_branch_info->commit)\n+\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n+\t\tnew_tree = parse_tree_indirect(the_hash_algo->empty_tree);\n+\t} else\n+\t\tnew_tree = get_commit_tree(new_branch_info->commit);\n \tif (opts->discard_changes) {\n-\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n-\t\t\t\t opts, 1, writeout_error);\n+\t\tret = reset_tree(new_tree, opts, 1, writeout_error);\n \t\tif (ret)\n \t\t\treturn ret;\n \t} else {\n@@ -614,7 +621,8 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\t\t   &old_branch_info->commit->object.oid :\n \t\t\t\t\t   the_hash_algo->empty_tree);\n \t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n-\t\ttree = parse_tree_indirect(&new_branch_info->commit->object.oid);\n+\t\tparse_tree(new_tree);\n+\t\ttree = new_tree;\n \t\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n \n \t\tret = unpack_trees(2, trees, &topts);\n@@ -663,7 +671,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\n \n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 1,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\n@@ -672,13 +680,13 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.branch1 = new_branch_info->name;\n \t\t\to.branch2 = \"local\";\n \t\t\tret = merge_trees(&o,\n-\t\t\t\t\t  get_commit_tree(new_branch_info->commit),\n+\t\t\t\t\t  new_tree,\n \t\t\t\t\t  work,\n \t\t\t\t\t  get_commit_tree(old_branch_info->commit),\n \t\t\t\t\t  &result);\n \t\t\tif (ret < 0)\n \t\t\t\texit(128);\n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tstrbuf_release(&o.obuf);\n@@ -696,7 +704,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->discard_changes && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet && new_branch_info->commit)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -897,7 +905,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \tadd_pending_object(&revs, object, oid_to_hex(&object->oid));\n \n \tfor_each_ref(add_pending_uninteresting_ref, &revs);\n-\tadd_pending_oid(&revs, \"HEAD\", &new_commit->object.oid, UNINTERESTING);\n+\tif (new_commit)\n+\t\tadd_pending_oid(&revs, \"HEAD\",\n+\t\t\t\t&new_commit->object.oid,\n+\t\t\t\tUNINTERESTING);\n \n \tif (prepare_revision_walk(&revs))\n \t\tdie(_(\"internal error in revision walk\"));\n@@ -932,6 +943,14 @@ static int switch_branches(const struct checkout_opts *opts,\n \tif (old_branch_info.path)\n \t\tskip_prefix(old_branch_info.path, \"refs/heads/\", &old_branch_info.name);\n \n+\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n+\t\tif (new_branch_info->name)\n+\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n+\t\tnew_branch_info->commit = NULL;\n+\t\tnew_branch_info->name = \"(empty)\";\n+\t\tdo_merge = 1;\n+\t}\n+\n \tif (!new_branch_info->name) {\n \t\tnew_branch_info->name = \"HEAD\";\n \t\tnew_branch_info->commit = old_branch_info.commit;\n@@ -1235,6 +1254,8 @@ static int checkout_branch(struct checkout_opts *opts,\n \tif (opts->new_orphan_branch) {\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--orphan\", \"-t\");\n+\t\tif (opts->orphan_from_empty_tree && new_branch_info->name)\n+\t\t\tdie(_(\"'%s' cannot take <start-point>\"), \"--orphan\");\n \t} else if (opts->force_detach) {\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n@@ -1539,6 +1560,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n+\topts.orphan_from_empty_tree = 0;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1577,6 +1599,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \topts.can_switch_when_in_progress = 0;\n+\topts.orphan_from_empty_tree = 1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372118","messageId":"20190321131655.15249-25-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 24/26] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:53Z","receivedAt":"2019-03-21T13:19:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"---\n t/t2060-switch.sh (new +x) | 98 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 98 insertions(+)\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..81e13cf7f0\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,98 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch master^{commit} &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\ttest_cmp_rev master^ refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\ttest_must_fail git switch -c temp &&\n+\tgit switch -C temp &&\n+\ttest_cmp_rev master refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch from empty' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch --orphan new-orphan HEAD &&\n+\tgit switch --orphan new-orphan &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit &&\n+\tgit ls-files >tracked-files &&\n+\techo orphan.t >expected &&\n+\ttest_cmp expected tracked-files\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch --no-guess foo &&\n+\tgit switch foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'switching when something is in progress' '\n+\ttest_when_finished rm -f .git/MERGE_HEAD &&\n+\t# fake a merge-in-progress\n+\tcp .git/HEAD .git/MERGE_HEAD &&\n+\ttest_must_fail git switch -d @^ &&\n+\tgit switch --ignore-in-progress -d @^ &&\n+\tgit switch -\n+'\n+\n+test_done\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372119","messageId":"20190321131655.15249-26-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 25/26] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:54Z","receivedAt":"2019-03-21T13:20:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n---\n contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-\n 1 file changed, 36 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 976e4a6548..b24bc48276 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -37,7 +37,8 @@\n #   GIT_COMPLETION_CHECKOUT_NO_GUESS\n #\n #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n-#     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n+#     and git-switch completion (e.g., completing \"foo\" when \"origin/foo\"\n+#     exists).\n \n case \"$COMP_WORDBREAKS\" in\n *:*) : great ;;\n@@ -2158,6 +2159,40 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if --track, --no-track, or --no-guess was specified\n+\t\t# if so, disable DWIM mode\n+\t\tlocal track_opt=\"--track\" only_local_ref=n\n+\t\tif [ \"$GIT_COMPLETION_CHECKOUT_NO_GUESS\" = \"1\" ] ||\n+\t\t   [ -n \"$(__git_find_on_cmdline \"--track --no-track --no-guess\")\" ]; then\n+\t\t\ttrack_opt=''\n+\t\tfi\n+\t\t# explicit --guess enables DWIM mode regardless of\n+\t\t# $GIT_COMPLETION_CHECKOUT_NO_GUESS\n+\t\tif [ -n \"$(__git_find_on_cmdline \"--guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\tfi\n+\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y -a -z \"$track_opt\" ]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372120","messageId":"20190321131655.15249-27-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v5 26/26] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-21T13:16:55Z","receivedAt":"2019-03-21T13:20:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n---\n Documentation/git-branch.txt           | 12 +++---\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 ++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 19 +++++----\n Documentation/giteveryday.txt          | 24 +++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/gitworkflows.txt         |  3 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 56 +++++++++++++-------------\n advice.c                               | 10 ++---\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             | 20 ++++-----\n 18 files changed, 101 insertions(+), 101 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0cd87ddeff..1e2d89b174 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -297,7 +297,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>\n NOTES\n -----\n \n-If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n-a branch and check it out with a single command.\n+If you are creating a branch that you want to switch to immediately,\n+it is easier to use the \"git switch\" command with its `-c` option to\n+do the same thing with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n serve four related but different purposes:\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..0a24a5679e 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 5629ba4c5d..cb6fc166e2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 132f8e55f6..cbf901efb4 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..f880d21dfb 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n-$ git reset --hard master^2\n-$ git checkout master\n+$ git switch -C mybranch master^2\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..ad455f3e39 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch -C pu next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt\nindex ca11c7bdaf..abc0dc6bc7 100644\n--- a/Documentation/gitworkflows.txt\n+++ b/Documentation/gitworkflows.txt\n@@ -301,8 +301,7 @@ topics on 'next':\n .Rewind and rebuild next\n [caption=\"Recipe: \"]\n =====================================\n-* `git checkout next`\n-* `git reset --hard master`\n+* `git switch -C next master`\n * `git merge ai/topic_in_next1`\n * `git merge ai/topic_in_next2`\n * ...\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..94799faa2b 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n-accept an arbitrary commit; for example, you can check out the commit\n-referenced by a tag:\n+The `git switch` command normally expects a branch head, but will also\n+accept an arbitrary commit when invoked with --detach; for example,\n+you can check out the commit referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex b224825637..27e39e6514 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\"\n+\t_(\"Note: switching to '%s'.\\n\"\n \t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"state without impacting any branches by switching back to a branch.\\n\"\n \t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\"\n \t\"\\n\"\n \t\"Or undo this checkout with:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -\\n\"\n+\t\"  git switch -\\n\"\n \t\"\\n\"\n \t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..da0518c8e3 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 743c7248a2..da6809b48f 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tOr undo this checkout with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \n@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n \tOr undo this checkout with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \n-- \n2.21.0.548.gd3c7d92dc2\n\n"},{"id":"372121","messageId":"CABPp-BHRh9bfMMhaLsb3btOVo6mpUEJ85w8VKNyds3AAZV+qqg@mail.gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"Re: [PATCH v5 00/26] Add new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-21T13:32:00Z","receivedAt":"2019-03-21T13:32:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Mar 21, 2019 at 6:17 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> This adds a new command 'git-switch' as the half-replacement for\n> 'git-checkout'. Jump to 12/26 as the starting point. The other half is\n> git-restore, which is dealt with separately.\n>\n> The diff delta is shrinking nicely. The two main changes are\n\nI only looked over the range-diff this time, but this round looks good\nto me.  Thanks for working on this.\n"},{"id":"372122","messageId":"CABPp-BHsQqi1umpsr1LREzwea15zqn4pp4WjKsTx3APf-4rT5Q@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8BdPgPgaWW=3_y5peLdqo24-ozC=s90WW2DN9OsYe9v_w@mail.gmail.com","subject":"Re: [PATCH] checkout.txt: note about losing staged changes with --merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-21T13:46:01Z","receivedAt":"2019-03-21T13:46:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Mar 20, 2019 at 7:58 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Mar 20, 2019 at 8:53 PM Elijah Newren <newren@gmail.com> wrote:\n> > So, I think we do need something (eventually at least).  Would you\n> > prefer we dropped this patch from Duy and instead made 'checkout -m'\n> > abort when the index is dirty?\n>\n> I have no problem with this. Still scratching my head wondering if\n> t7201-co.sh has a slightly incorrect setup, or aborting is actually\n> wrong. You're probably a better person to understand that test case\n> ;-)\n\nIt doesn't surprise me at all that some testcases would fail with this\nchange; it's a change of behavior from the previous implementation.\nHowever, taking a look at that testcase, it looks like it's not a\nsimple change to make it do something similar because there's at least\none other bug that we need to fix first.  I'll dig in...though I\nreally want to get my\ndirectory-rename-detection-defaults-to-reporting-conflict series\nupdated and sent out first.  Since Junio seems to be okay with just\ntaking your doc update for now, hopefully that's not a problem.  :-)\n"},{"id":"372199","messageId":"xmqqh8bvr0v6.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"Re: [PATCH v5 00/26] Add new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-22T04:26:05Z","receivedAt":"2019-03-22T04:26:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This adds a new command 'git-switch' as the half-replacement for\n> 'git-checkout'. Jump to 12/26 as the starting point. The other half is\n> git-restore, which is dealt with separately.\n>\n> The diff delta is shrinking nicely. The two main changes are\n>\n> - '--orphan <new-branch> <initial-tree>' is no longer accepted\n> - --force implies --ignore-in-progress\n>\n> The rest is the usual typo and bug fixes.\n\nAnd droppage of sign-off?\n\nOther than that, the changes relative to the previous round looked\nall reasonable.\n"},{"id":"372207","messageId":"CACsJy8B2an6G0RgCF_KW2ktXrm9Vn3o_Uir+_o2SMUdmudt4iA@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqh8bvr0v6.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 00/26] Add new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-22T07:30:02Z","receivedAt":"2019-03-22T07:30:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 22, 2019 at 11:26 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > This adds a new command 'git-switch' as the half-replacement for\n> > 'git-checkout'. Jump to 12/26 as the starting point. The other half is\n> > git-restore, which is dealt with separately.\n> >\n> > The diff delta is shrinking nicely. The two main changes are\n> >\n> > - '--orphan <new-branch> <initial-tree>' is no longer accepted\n> > - --force implies --ignore-in-progress\n> >\n> > The rest is the usual typo and bug fixes.\n>\n> And droppage of sign-off?\n\nI think the amount of options I have to give to format-patch starts to\noverwhelm me. Can you forge my sign-off? I'll resend otherwise.\n-- \nDuy\n"},{"id":"372209","messageId":"46b16732-c736-fae0-2c25-fb6528f72797@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-27-pclouds@gmail.com","subject":"Re: [PATCH v5 26/26] doc: promote \"git switch\"","fromName":"Andrei Rybak","fromEmail":"rybak.a.v@gmail.com","sentAt":"2019-03-22T08:00:09Z","receivedAt":"2019-03-22T08:00:17Z","isPatch":true,"sender":{"key":"rybak.a.v@gmail.com","avatar":"https://avatars.githubusercontent.com/u/624072?v=4"},"body":"On 3/21/19 2:16 PM, Nguyễn Thái Ngọc Duy wrote:\n> ...\n>\n> diff --git a/advice.c b/advice.c\n> index b224825637..27e39e6514 100644\n> --- a/advice.c\n> +++ b/advice.c\n> @@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)\n>  void detach_advice(const char *new_name)\n>  {\n>  \tconst char *fmt =\n> -\t_(\"Note: checking out '%s'.\\n\"\n> +\t_(\"Note: switching to '%s'.\\n\"\n>  \t\"\\n\"\n>  \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n>  \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n> -\t\"state without impacting any branches by performing another checkout.\\n\"\n> +\t\"state without impacting any branches by switching back to a branch.\\n\"\n>  \t\"\\n\"\n>  \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n> -\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n> +\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n>  \t\"\\n\"\n> -\t\"  git checkout -b <new-branch-name>\\n\"\n> +\t\"  git switch -c <new-branch-name>\\n\"\n>  \t\"\\n\"\n>  \t\"Or undo this checkout with:\\n\"\n\nWith the start of the message being \"switching to ...\" this part could probably\nbe also updated to something like \"Or undo this switch with\" or \"Or undo this\nswitch or checkout with\".\n\n>  \t\"\\n\"\n> -\t\"  git checkout -\\n\"\n> +\t\"  git switch -\\n\"\n>  \t\"\\n\"\n>  \t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n>  \n\n"},{"id":"372212","messageId":"CACsJy8DJVtdtCz3=6aRoYUbPaBV+oiEh0cP6XrTrtS7w14OSYA@mail.gmail.com","threadId":"50360","inReplyTo":"46b16732-c736-fae0-2c25-fb6528f72797@gmail.com","subject":"Re: [PATCH v5 26/26] doc: promote \"git switch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-22T09:12:17Z","receivedAt":"2019-03-22T09:12:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 22, 2019 at 3:00 PM Andrei Rybak <rybak.a.v@gmail.com> wrote:\n>\n> On 3/21/19 2:16 PM, Nguyễn Thái Ngọc Duy wrote:\n> > ...\n> >\n> > diff --git a/advice.c b/advice.c\n> > index b224825637..27e39e6514 100644\n> > --- a/advice.c\n> > +++ b/advice.c\n> > @@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)\n> >  void detach_advice(const char *new_name)\n> >  {\n> >       const char *fmt =\n> > -     _(\"Note: checking out '%s'.\\n\"\n> > +     _(\"Note: switching to '%s'.\\n\"\n> >       \"\\n\"\n> >       \"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n> >       \"changes and commit them, and you can discard any commits you make in this\\n\"\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> >       \"\\n\"\n> >       \"If you want to create a new branch to retain commits you create, you may\\n\"\n> > -     \"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n> > +     \"do so (now or later) by using -c with the switch command. Example:\\n\"\n> >       \"\\n\"\n> > -     \"  git checkout -b <new-branch-name>\\n\"\n> > +     \"  git switch -c <new-branch-name>\\n\"\n> >       \"\\n\"\n> >       \"Or undo this checkout with:\\n\"\n>\n> With the start of the message being \"switching to ...\" this part could probably\n> be also updated to something like \"Or undo this switch with\" or \"Or undo this\n> switch or checkout with\".\n\nMaybe the neutral \"operation\"? E.g. \"Or undo this operation with:\"\n\n>\n> >       \"\\n\"\n> > -     \"  git checkout -\\n\"\n> > +     \"  git switch -\\n\"\n> >       \"\\n\"\n> >       \"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n> >\n>\n\n\n-- \nDuy\n"},{"id":"372514","messageId":"CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com","threadId":"50360","inReplyTo":"7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-26T12:50:31Z","receivedAt":"2019-03-26T12:51:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > \"git checkout\" doing too many things is a source of confusion for many\n> > users (and it even bites old timers sometimes). To remedy that, the\n> > command will be split into two new ones: switch and\n> > something-to-checkout-paths.\n>\n> I think this is a good idea, thanks for working on it. I wonder if it\n> would be a good idea to have the new command refuse to checkout a new\n> branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> an option to override the check) as switching branches in the middle of\n> one of those is likely to be confusing to users (if I do it it is\n> normally because I've forgotten that I've not run 'git whatever\n> --continue').\n\nGuys, I'm sorry for bringing this up again. Apparently I'm not quite\ndone with 'git switch' yet (not sure if I will ever be).\n\nThere is an interesting behavior in git-checkout (and of couse\ngit-switch). When you do a successful switch, CHERRY_PICK_HEAD,\nREVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and\nSQUAHS_MSG, if exists, will be removed.\n\nThis basically means that if you switch away, any cherry-pick, merge\nor revert in progress is destroyed (in the sense of \"--quit\" not\n\"--abort\" of course). All of this, I believe, involves merge conflicts\nso you can't easily switch away unless you allow to destroy unmerged\nentries. So it's still quite safe.\n\nHowever, it leaves me a funny feeling because some \"work-in-progress\"\noperations are destroyed, but some others (bisect, rebase) are not.\nThis is git-checkout behavior and I will not change that. But do we\nwant the same behavior in git-switch? Or do we want\nno-destroy-in-progress-whatsoever? Or\ndestroy-all-commands-in-progress?\n\nIt may also be a good idea to attempt to describe the behavior we want\nin git-switch.txt. I think if the description gets too complicated,\nwe're heading a wrong way. The current behavior so far could still be\ndescribed as \"work-in-progress operations related to merge conflicts\nare destroyed\", or something along that line. But I'm not quite\nconvinced it's easily understood.\n\nPS. git-reset shares the same behavior, but it's in a different boat,\nI think. Or maybe I should scrap/replace that one as well.\n\nPPS. It may get trickier with cherry-pick which can pick a range of\ncommits now, not just one. But I never used that feature much to know\nwhat I'm talking about.\n-- \nDuy\n"},{"id":"372522","messageId":"CABPp-BEksf4SuD57YsUO3YKhU12CAwFTy6pA1tETFrHB1DAz9w@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-26T15:01:24Z","receivedAt":"2019-03-26T15:01:39Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 26, 2019 at 5:50 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > > \"git checkout\" doing too many things is a source of confusion for many\n> > > users (and it even bites old timers sometimes). To remedy that, the\n> > > command will be split into two new ones: switch and\n> > > something-to-checkout-paths.\n> >\n> > I think this is a good idea, thanks for working on it. I wonder if it\n> > would be a good idea to have the new command refuse to checkout a new\n> > branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> > an option to override the check) as switching branches in the middle of\n> > one of those is likely to be confusing to users (if I do it it is\n> > normally because I've forgotten that I've not run 'git whatever\n> > --continue').\n>\n> Guys, I'm sorry for bringing this up again. Apparently I'm not quite\n> done with 'git switch' yet (not sure if I will ever be).\n>\n> There is an interesting behavior in git-checkout (and of couse\n> git-switch). When you do a successful switch, CHERRY_PICK_HEAD,\n> REVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and\n> SQUAHS_MSG, if exists, will be removed.\n>\n> This basically means that if you switch away, any cherry-pick, merge\n> or revert in progress is destroyed (in the sense of \"--quit\" not\n> \"--abort\" of course). All of this, I believe, involves merge conflicts\n> so you can't easily switch away unless you allow to destroy unmerged\n> entries. So it's still quite safe.\n>\n> However, it leaves me a funny feeling because some \"work-in-progress\"\n> operations are destroyed, but some others (bisect, rebase) are not.\n> This is git-checkout behavior and I will not change that. But do we\n> want the same behavior in git-switch? Or do we want\n> no-destroy-in-progress-whatsoever? Or\n> destroy-all-commands-in-progress?\n\nI thought we already decided that we'd abort the switch if there was\nany operation-in-progress state?  Or are you asking what should we do\nif the user explicitly overrides this error with e.g.\n--ignore-in-progress?  In that case, I'd say that the reasonable thing\nto do would be to leave all the state files alone.  If we make it\nclear out the state, we're simply combining uncommon commands for the\nuser (<operation> --quit + git switch), which seems like a bad UI path\nto go down.  Allowing them to switch to some other branch while\nkeeping all state files is something they can't do with any other\ncommand, and while I hope people wouldn't want to do that much,\nswitching while keeping state files is something that can't be done\nwith combining other commands and thus at least makes sense as\nsomething to consider providing.\n\n> It may also be a good idea to attempt to describe the behavior we want\n> in git-switch.txt. I think if the description gets too complicated,\n> we're heading a wrong way. The current behavior so far could still be\n> described as \"work-in-progress operations related to merge conflicts\n> are destroyed\", or something along that line. But I'm not quite\n> convinced it's easily understood.\n>\n> PS. git-reset shares the same behavior, but it's in a different boat,\n> I think. Or maybe I should scrap/replace that one as well.\n\nreset has traditionally been the home of\nhow-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick\nor revert was 'reset --hard' (or later 'reset --merge'), skipping a\nbecome-empty cherry-pick or rebase is still 'reset', etc.  So it's not\nthat surprising to me that it clears out state.  As to whether it\nstill should, we can address that along with...\n\n> PPS. It may get trickier with cherry-pick which can pick a range of\n> commits now, not just one. But I never used that feature much to know\n> what I'm talking about.\n\nYeah, cherry-pick and revert and rebase all allow working on several\nitems.  It seems bad to me to abort/quit such operations without an\nexplicit '(cherry-pick|rebase|revert) --(abort|quit)'.  Back when\ncherry-pick and revert only worked on a single commit, allowing 'reset\n--hard' to clear state didn't seem that unreasonable even if it wasn't\nthe best UI.  So it all went there until people got used to 'rebase\n--abort' and realized it was a better way and started spreading\n--abort to other commands.  And you could kind of see us overlooking\nthat someone could cherry-pick --no-commit and then afterwards\ncheckout --force to put those changes somewhere else but I think it\nwas even dangerous/weird back then.  Once cherry-pick and revert\ncommands were extended to work with multiple commits like rebase does,\nI just don't think it makes sense to allow reset to completely clear\nout state files anymore.  Also, I don't want to check for one or\nmultiple commits and make them behave differently; I want git to\nconsistently require an explicit command-specific abort or quit (or\ncontinue) to clear out the state associated with those\npossibly-multi-step operations.  Maybe 'reset --hard' can continue\nclearing out merge state just by force of habit, though I'd still\nrather encourage 'merge --abort'.  But I don't think it makes sense to\nlet switch, reset, etc. remove multi-step progress state.\n"},{"id":"372524","messageId":"CACsJy8DPDEvNDeE5MpqcGZk9jRmT9g=ix+MOhkv+50J3Egef7A@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BEksf4SuD57YsUO3YKhU12CAwFTy6pA1tETFrHB1DAz9w@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-26T15:24:14Z","receivedAt":"2019-03-26T15:24:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Tue, Mar 26, 2019 at 5:50 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > On Mon, Mar 11, 2019 at 6:16 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > > On 08/03/2019 09:57, Nguyễn Thái Ngọc Duy wrote:\n> > > > \"git checkout\" doing too many things is a source of confusion for many\n> > > > users (and it even bites old timers sometimes). To remedy that, the\n> > > > command will be split into two new ones: switch and\n> > > > something-to-checkout-paths.\n> > >\n> > > I think this is a good idea, thanks for working on it. I wonder if it\n> > > would be a good idea to have the new command refuse to checkout a new\n> > > branch if there is a cherry-pick/revert/merge/rebase in progress (with\n> > > an option to override the check) as switching branches in the middle of\n> > > one of those is likely to be confusing to users (if I do it it is\n> > > normally because I've forgotten that I've not run 'git whatever\n> > > --continue').\n> >\n> > Guys, I'm sorry for bringing this up again. Apparently I'm not quite\n> > done with 'git switch' yet (not sure if I will ever be).\n> >\n> > There is an interesting behavior in git-checkout (and of couse\n> > git-switch). When you do a successful switch, CHERRY_PICK_HEAD,\n> > REVERT_HEAD, MERGE_HEAD, MERGE_RR, MERGE_MSG, MERGE_MODE and\n> > SQUAHS_MSG, if exists, will be removed.\n> >\n> > This basically means that if you switch away, any cherry-pick, merge\n> > or revert in progress is destroyed (in the sense of \"--quit\" not\n> > \"--abort\" of course). All of this, I believe, involves merge conflicts\n> > so you can't easily switch away unless you allow to destroy unmerged\n> > entries. So it's still quite safe.\n> >\n> > However, it leaves me a funny feeling because some \"work-in-progress\"\n> > operations are destroyed, but some others (bisect, rebase) are not.\n> > This is git-checkout behavior and I will not change that. But do we\n> > want the same behavior in git-switch? Or do we want\n> > no-destroy-in-progress-whatsoever? Or\n> > destroy-all-commands-in-progress?\n>\n> I thought we already decided that we'd abort the switch if there was\n> any operation-in-progress state?  Or are you asking what should we do\n> if the user explicitly overrides this error with e.g.\n> --ignore-in-progress?  In that case, I'd say that the reasonable thing\n> to do would be to leave all the state files alone.  If we make it\n> clear out the state, we're simply combining uncommon commands for the\n> user (<operation> --quit + git switch), which seems like a bad UI path\n> to go down.  Allowing them to switch to some other branch while\n> keeping all state files is something they can't do with any other\n> command, and while I hope people wouldn't want to do that much,\n> switching while keeping state files is something that can't be done\n> with combining other commands and thus at least makes sense as\n> something to consider providing.\n\nYeah.. --ignore-in-process does not necessarily mean aborting\nsomething when you just want to get out to examine some other commit.\nAnd I agree doing nothing seems like the best (or least\nconfusing/surprising) option.\n\nThere will be some funny thing. Like if you commit after switching\naway and MERGE_HEAD is there, I think you will be creating a merge\ncommit.\n\nBut I guess if you choose --ignore-in-progress, you're pretty much on\nyour own. We could improve \"git commit\" and friends to realize that\nthe current state is unlikely safe to resume whatever in-progress\noperations. But that's separate and I don't want to dig another hole\nto bury myself in while there's still 'git restore' to take care of.\n\nOr maybe I'll just suggest \"git <verb> --quit\" when the switch is\nsuccessful with --ignore-in-progress.\n\nWill wait a while for people to comment (and work on that 'git switch\n--recurse-submodules', which seems also needs some work) before\nresubmitting.\n\n> > It may also be a good idea to attempt to describe the behavior we want\n> > in git-switch.txt. I think if the description gets too complicated,\n> > we're heading a wrong way. The current behavior so far could still be\n> > described as \"work-in-progress operations related to merge conflicts\n> > are destroyed\", or something along that line. But I'm not quite\n> > convinced it's easily understood.\n> >\n> > PS. git-reset shares the same behavior, but it's in a different boat,\n> > I think. Or maybe I should scrap/replace that one as well.\n>\n> reset has traditionally been the home of\n> how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick\n> or revert was 'reset --hard' (or later 'reset --merge'), skipping a\n> become-empty cherry-pick or rebase is still 'reset', etc.  So it's not\n> that surprising to me that it clears out state.\n> ...\n\nYeah but it was surprising to me that this is not even mentioned\nanywhere in git-reset.txt. You learn by examples basically, or by\nexperience. But I digress.\n-- \nDuy\n"},{"id":"372526","messageId":"CABPp-BFTyALWmnJ=dT1xNivjcQhtKak15ydfkYjEsEC-j4BD9w@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8DPDEvNDeE5MpqcGZk9jRmT9g=ix+MOhkv+50J3Egef7A@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-26T15:48:23Z","receivedAt":"2019-03-26T15:48:37Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n\n> Yeah.. --ignore-in-process does not necessarily mean aborting\n> something when you just want to get out to examine some other commit.\n> And I agree doing nothing seems like the best (or least\n> confusing/surprising) option.\n>\n> There will be some funny thing. Like if you commit after switching\n> away and MERGE_HEAD is there, I think you will be creating a merge\n> commit.\n\nYes, and in the middle of a cherry-pick with a range you've added some\ncommits to one branch and some to another.  In the middle of a revert\nyou're doing similar.  It sounds like crazytown to me (and maybe we\nshouldn't provide the --ignore-in-process flag unless users clamor for\nit -- or maybe we should print a big warning whenever people use it\nmuch like we've traditionally done when people checkout a commit\nrather than a branch), but it at least makes sense what is happening\nand users had to explicitly state they wanted the in-progress state to\njust be ignored and switch anyway.\n\n> But I guess if you choose --ignore-in-progress, you're pretty much on\n> your own. We could improve \"git commit\" and friends to realize that\n> the current state is unlikely safe to resume whatever in-progress\n> operations. But that's separate and I don't want to dig another hole\n> to bury myself in while there's still 'git restore' to take care of.\n>\n> Or maybe I'll just suggest \"git <verb> --quit\" when the switch is\n> successful with --ignore-in-progress.\n\nCombining that with some kind of warning about weird effects of\napplying a mid-operation process across branches seems like a good\nidea to me if we're going to have that flag.\n\n> > > PS. git-reset shares the same behavior, but it's in a different boat,\n> > > I think. Or maybe I should scrap/replace that one as well.\n> >\n> > reset has traditionally been the home of\n> > how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick\n> > or revert was 'reset --hard' (or later 'reset --merge'), skipping a\n> > become-empty cherry-pick or rebase is still 'reset', etc.  So it's not\n> > that surprising to me that it clears out state.\n> > ...\n>\n> Yeah but it was surprising to me that this is not even mentioned\n> anywhere in git-reset.txt. You learn by examples basically, or by\n> experience. But I digress.\n\nYeah that is slightly odd -- but that at least provides a small silver\nlining: it makes it easier to decide to change it and move all the\nmid-operation-state-clearing to other commands.  :-)\n"},{"id":"372527","messageId":"CACsJy8B0ZWHWGbaE8eDAO_+xzATZRkwJTBGLZnQkR7jSrTv9-g@mail.gmail.com","threadId":"50360","inReplyTo":"CABPp-BFTyALWmnJ=dT1xNivjcQhtKak15ydfkYjEsEC-j4BD9w@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-26T15:57:26Z","receivedAt":"2019-03-26T15:57:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 26, 2019 at 10:48 PM Elijah Newren <newren@gmail.com> wrote:\n> > > > PS. git-reset shares the same behavior, but it's in a different boat,\n> > > > I think. Or maybe I should scrap/replace that one as well.\n> > >\n> > > reset has traditionally been the home of\n> > > how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick\n> > > or revert was 'reset --hard' (or later 'reset --merge'), skipping a\n> > > become-empty cherry-pick or rebase is still 'reset', etc.  So it's not\n> > > that surprising to me that it clears out state.\n> > > ...\n> >\n> > Yeah but it was surprising to me that this is not even mentioned\n> > anywhere in git-reset.txt. You learn by examples basically, or by\n> > experience. But I digress.\n>\n> Yeah that is slightly odd -- but that at least provides a small silver\n> lining: it makes it easier to decide to change it and move all the\n> mid-operation-state-clearing to other commands.  :-)\n\nDon't tempt me. Elsewhere in some discussion with Ævar I wrote it's\nbetter to add \"git-ng\" instead of going through the deprecation\nprocess to change behavior of current commands, which also means that\nyou better design git-ng well because you can't just go \"oops, i did\nit again\" and add \"git-nng\". And I'm slowly realizing that 'switch'\nand 'restore' are just \"git-ng checkout\" in disguise. That already\nincreases my stress level a bit.\n-- \nDuy\n"},{"id":"372565","messageId":"0dc8820c-c6b0-c4ca-2107-84061fdc5333@gmail.com","threadId":"50360","inReplyTo":"CABPp-BFTyALWmnJ=dT1xNivjcQhtKak15ydfkYjEsEC-j4BD9w@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-27T10:24:32Z","receivedAt":"2019-03-27T10:24:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 26/03/2019 15:48, Elijah Newren wrote:\n> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n> \n>> Yeah.. --ignore-in-process does not necessarily mean aborting\n>> something when you just want to get out to examine some other commit.\n>> And I agree doing nothing seems like the best (or least\n>> confusing/surprising) option.\n>>\n>> There will be some funny thing. Like if you commit after switching\n>> away and MERGE_HEAD is there, I think you will be creating a merge\n>> commit.\n> \n> Yes, and in the middle of a cherry-pick with a range you've added some\n> commits to one branch and some to another.  In the middle of a revert\n> you're doing similar.  It sounds like crazytown to me (and maybe we\n> shouldn't provide the --ignore-in-process flag unless users clamor for\n> it \n\nI think that could be the way to go for merges and cherry-picks, or\npossibly require --discard-changes as well. The only time I use checkout\nlike this is during a rebase if I want to rewind it - I edit the todo\nlist with the output of 'git log --pretty=\"pick %h %s\" --reverse' and do\n'git checkout' followed by 'git rebase --continue' Though these days I\ncould add a 'reset' line to the todo list and skip the checkout.\n\n> -- or maybe we should print a big warning whenever people use it\n> much like we've traditionally done when people checkout a commit\n> rather than a branch), but it at least makes sense what is happening\n> and users had to explicitly state they wanted the in-progress state to\n> just be ignored and switch anyway.\n> \n>> But I guess if you choose --ignore-in-progress, you're pretty much on\n>> your own. We could improve \"git commit\" and friends to realize that\n>> the current state is unlikely safe to resume whatever in-progress\n>> operations. But that's separate and I don't want to dig another hole\n>> to bury myself in while there's still 'git restore' to take care of.\n>>\n>> Or maybe I'll just suggest \"git <verb> --quit\" when the switch is\n>> successful with --ignore-in-progress.\n> \n> Combining that with some kind of warning about weird effects of\n> applying a mid-operation process across branches seems like a good\n> idea to me if we're going to have that flag.\n> \n>>>> PS. git-reset shares the same behavior, but it's in a different boat,\n>>>> I think. Or maybe I should scrap/replace that one as well.\n>>>\n>>> reset has traditionally been the home of\n>>> how-to-clear-in-progress-state.  e.g. aborting a merge or cherry-pick\n>>> or revert was 'reset --hard' (or later 'reset --merge'), skipping a\n>>> become-empty cherry-pick or rebase is still 'reset', etc.  So it's not\n>>> that surprising to me that it clears out state.\n>>> ...\n>>\n>> Yeah but it was surprising to me that this is not even mentioned\n>> anywhere in git-reset.txt. You learn by examples basically, or by\n>> experience. But I digress.\n> \n> Yeah that is slightly odd -- but that at least provides a small silver\n> lining: it makes it easier to decide to change it and move all the\n> mid-operation-state-clearing to other commands.  :-)\n\nThe missing documentation is odd but I think it makes sense. 'reset\n--hard' throws away all the changes in the index and worktree so it\nwould be confusing if MERGE_HEAD was left behind. 'reset --mixed' throws\naway the changes in the index and you cannot do a partial commit if\nMERGE_HEAD or CHERRY_PICK_HEAD exist so it probably makes sense there as\nwell (although if you do 'git reset && git commit -a' it wont remember\nthe merge/cherry-pick). Some of the other reset modes (--soft and --keep\nI think) detect on in progress merge and refuse to run, I've got a patch\nsomewhere that extends that to cherry-picks. I should probably extend it\nto handle --merge as well (are there any other modes, I only ever use\n--hard/--mixed/--soft as I'm never sure what the others will do)\n\nBest Wishes\n\nPhillip\n\n"},{"id":"372610","messageId":"CACsJy8AmgDh1Z4CKTzSAVrywo0q-CFKbpuyqdtRm9m7pkUweSw@mail.gmail.com","threadId":"50360","inReplyTo":"0dc8820c-c6b0-c4ca-2107-84061fdc5333@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-28T11:04:12Z","receivedAt":"2019-03-28T11:04:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 26/03/2019 15:48, Elijah Newren wrote:\n> > On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n> >\n> >> Yeah.. --ignore-in-process does not necessarily mean aborting\n> >> something when you just want to get out to examine some other commit.\n> >> And I agree doing nothing seems like the best (or least\n> >> confusing/surprising) option.\n> >>\n> >> There will be some funny thing. Like if you commit after switching\n> >> away and MERGE_HEAD is there, I think you will be creating a merge\n> >> commit.\n> >\n> > Yes, and in the middle of a cherry-pick with a range you've added some\n> > commits to one branch and some to another.  In the middle of a revert\n> > you're doing similar.  It sounds like crazytown to me (and maybe we\n> > shouldn't provide the --ignore-in-process flag unless users clamor for\n> > it\n\nI missed this part in my last reading. I think if we could safely\nswitch away and get back to resume, then --ignore-in-process could\nstill be useful. I sometimes switch to another commit to check out\nstuff then back. For interactive rebase with \"edit\" command for\nexample, it's quite safe to do so. (yes the other option is \"git\nworktree add\", but that could be a heavy hammer sometimes)\n\n> I think that could be the way to go for merges and cherry-picks, or\n\nJust so we're clear, what is your \"the way\" to go? to remove\nCHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n--ignore-in-process is specified? Or to leave MERGE_* and\nCHERRY_PICK_HEAD alone and delete other stuff?\n\n> possibly require --discard-changes as well. The only time I use checkout\n> like this is during a rebase if I want to rewind it - I edit the todo\n> list with the output of 'git log --pretty=\"pick %h %s\" --reverse' and do\n> 'git checkout' followed by 'git rebase --continue' Though these days I\n> could add a 'reset' line to the todo list and skip the checkout.\n-- \nDuy\n"},{"id":"372622","messageId":"CABPp-BHQCy4zUEhFvLpjs8vMFq7vTaHzbe6BoJxCShxYtk7K9w@mail.gmail.com","threadId":"50360","inReplyTo":"CACsJy8AmgDh1Z4CKTzSAVrywo0q-CFKbpuyqdtRm9m7pkUweSw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-28T15:05:27Z","receivedAt":"2019-03-28T15:05:42Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Mar 28, 2019 at 4:04 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >\n> > On 26/03/2019 15:48, Elijah Newren wrote:\n> > > On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > >> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n> > >\n> > >> Yeah.. --ignore-in-process does not necessarily mean aborting\n> > >> something when you just want to get out to examine some other commit.\n> > >> And I agree doing nothing seems like the best (or least\n> > >> confusing/surprising) option.\n> > >>\n> > >> There will be some funny thing. Like if you commit after switching\n> > >> away and MERGE_HEAD is there, I think you will be creating a merge\n> > >> commit.\n> > >\n> > > Yes, and in the middle of a cherry-pick with a range you've added some\n> > > commits to one branch and some to another.  In the middle of a revert\n> > > you're doing similar.  It sounds like crazytown to me (and maybe we\n> > > shouldn't provide the --ignore-in-process flag unless users clamor for\n> > > it\n>\n> I missed this part in my last reading. I think if we could safely\n> switch away and get back to resume, then --ignore-in-process could\n> still be useful. I sometimes switch to another commit to check out\n> stuff then back. For interactive rebase with \"edit\" command for\n> example, it's quite safe to do so. (yes the other option is \"git\n> worktree add\", but that could be a heavy hammer sometimes)\n\nAh, switching not for the purpose of continuing the operation\nelsewhere, but just to check things out with the intent of coming back\nbefore continuing.  That does make sense.  Please do add a warning\nwhen users do it, though.\n\n> > I think that could be the way to go for merges and cherry-picks, or\n>\n> Just so we're clear, what is your \"the way\" to go? to remove\n> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n> --ignore-in-process is specified? Or to leave MERGE_* and\n> CHERRY_PICK_HEAD alone and delete other stuff?\n\nPersonally, I don't think `switch` should modify in-process state of\nother operations ever.  Not merge state, not cherry-pick state, not\nrebase state, not bisect state, etc.  And default should be for it to\nerror out if any such state exists.  In-process state should be\nupdated almost exclusively by `<operation>\n--(continue|abort|skip|quit)` (or in bisect's case, the\nbad/good/skip/reset commands).  They only reason I put the 'almost'\nqualifier in there is that I'm not opposed to the _reset_ command\ncontinuing to do some things with state because of backward\ncompatibility (and I might begrudgingly admit the same for other\ncommands if they touch it though I'm tempted to say any command other\nthan reset and the operation itself that touches in-process state\nrepresents a bug that should be fixed).  However, I very much want to\nsteer users away from using reset (or other commands) to change state\nand guide them to instead use `<operation>\n--(continue|abort|skip|quit)`.  I think we make things confusing and\ndo users a disservice by not having in-process state be a totally\northogonal thing that is only updated by that process's subcommand.\n\nIf people use --ignore-in-process, I think the state should remain and\nwe should give a big warning to the user about how the operation is\nstill in progress and likely to give very confusing results and\nsuggest either switching back to the current commit or running\n`<operation> --quit` for them to clear it.\n\nAlso, if --ignore-in-process still fails because they need\n--discard-changes too, then we could give a really obnoxious warning\nabout how they should probably consider --quit first if they really\nwant to switch.\n\n> > possibly require --discard-changes as well. The only time I use checkout\n> > like this is during a rebase if I want to rewind it - I edit the todo\n> > list with the output of 'git log --pretty=\"pick %h %s\" --reverse' and do\n> > 'git checkout' followed by 'git rebase --continue' Though these days I\n> > could add a 'reset' line to the todo list and skip the checkout.\n\nI think Phillip's usecase is entirely consistent with the\ndon't-have-switch-touch-process-state view.\n\n\nHope that helps,\nElijah\n"},{"id":"372632","messageId":"a68ce0b4-81c5-e889-fc90-ed8b17a10d4a@gmail.com","threadId":"50360","inReplyTo":"CACsJy8AmgDh1Z4CKTzSAVrywo0q-CFKbpuyqdtRm9m7pkUweSw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-28T16:23:13Z","receivedAt":"2019-03-28T16:23:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 28/03/2019 11:04, Duy Nguyen wrote:\n> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 26/03/2019 15:48, Elijah Newren wrote:\n>>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n>>>\n>>>> Yeah.. --ignore-in-process does not necessarily mean aborting\n>>>> something when you just want to get out to examine some other commit.\n>>>> And I agree doing nothing seems like the best (or least\n>>>> confusing/surprising) option.\n>>>>\n>>>> There will be some funny thing. Like if you commit after switching\n>>>> away and MERGE_HEAD is there, I think you will be creating a merge\n>>>> commit.\n>>>\n>>> Yes, and in the middle of a cherry-pick with a range you've added some\n>>> commits to one branch and some to another.  In the middle of a revert\n>>> you're doing similar.  It sounds like crazytown to me (and maybe we\n>>> shouldn't provide the --ignore-in-process flag unless users clamor for\n>>> it\n> \n> I missed this part in my last reading. I think if we could safely\n> switch away and get back to resume, then --ignore-in-process could\n> still be useful.\n\nIf we can get back safely then that makes sense, I'm not sure about \nswitching while there are conflicts or staged changes though, it feels \nlike there's more potential for things to go wrong there.\n\n> I sometimes switch to another commit to check out\n> stuff then back. For interactive rebase with \"edit\" command for\n> example, it's quite safe to do so. (yes the other option is \"git\n> worktree add\", but that could be a heavy hammer sometimes) >\n>> I think that could be the way to go for merges and cherry-picks, or\n> \n> Just so we're clear, what is your \"the way\" to go? to remove\n> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n> --ignore-in-process is specified? Or to leave MERGE_* and\n> CHERRY_PICK_HEAD alone and delete other stuff?\n\nI was agreeing with Elijah about dropping --ignore-in-progress unless \nthere's a demand for it or at least restricting it so that it requires \n--discard-changes and aborts in-progress merges and single in-progress \ncherry-picks/reverts. (I'm worried about people switching branches when \ncherry-picking more than one commit, though as you say it can make sense \nduring a rebase.)\n\nBest Wishes\n\nPhillip\n\n>> possibly require --discard-changes as well. The only time I use checkout\n>> like this is during a rebase if I want to rewind it - I edit the todo\n>> list with the output of 'git log --pretty=\"pick %h %s\" --reverse' and do\n>> 'git checkout' followed by 'git rebase --continue' Though these days I\n>> could add a 'reset' line to the todo list and skip the checkout.\n"},{"id":"372637","messageId":"CABPp-BHsD=o=3jPKH6gH+XbEdNVqzhr18BSfupbaSNpxAgaqdw@mail.gmail.com","threadId":"50360","inReplyTo":"a68ce0b4-81c5-e889-fc90-ed8b17a10d4a@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-28T17:39:45Z","receivedAt":"2019-03-28T17:40:01Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Phillip,\n\nOn Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 28/03/2019 11:04, Duy Nguyen wrote:\n> > On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >> On 26/03/2019 15:48, Elijah Newren wrote:\n> >>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n\n> >>> Yes, and in the middle of a cherry-pick with a range you've added some\n> >>> commits to one branch and some to another.  In the middle of a revert\n> >>> you're doing similar.  It sounds like crazytown to me (and maybe we\n> >>> shouldn't provide the --ignore-in-process flag unless users clamor for\n> >>> it\n> >\n> > I missed this part in my last reading. I think if we could safely\n> > switch away and get back to resume, then --ignore-in-process could\n> > still be useful.\n>\n> If we can get back safely then that makes sense, I'm not sure about\n> switching while there are conflicts or staged changes though, it feels\n> like there's more potential for things to go wrong there.\n\nI really like that way of putting it; I think that makes it much\nclearer.  Note, though that staged changes and conflicts could happen\nwith any of rebase, merge, cherry-pick, or revert, so this problem is\nnot limited to a subset of those operations.\n\n> > I sometimes switch to another commit to check out\n> > stuff then back. For interactive rebase with \"edit\" command for\n> > example, it's quite safe to do so. (yes the other option is \"git\n> > worktree add\", but that could be a heavy hammer sometimes)\n>\n> >> I think that could be the way to go for merges and cherry-picks, or\n> >\n> > Just so we're clear, what is your \"the way\" to go? to remove\n> > CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n> > --ignore-in-process is specified? Or to leave MERGE_* and\n> > CHERRY_PICK_HEAD alone and delete other stuff?\n>\n> I was agreeing with Elijah about dropping --ignore-in-progress unless\n> there's a demand for it or at least restricting it so that it requires\n> --discard-changes and aborts in-progress merges and single in-progress\n> cherry-picks/reverts. (I'm worried about people switching branches when\n> cherry-picking more than one commit, though as you say it can make sense\n> during a rebase.)\n\nI understand the desire to prevent mis-uses, and I agree that if there\nare staged changes or conflicts it's really likely things will go\nsideways.  But I think we should instead check for those situations\nrather than use e.g. rebase vs. merge as a proxy for whether those\nproblems could be present.  I am especially concerned with the idea of\nhaving something like \"git switch --ignore-in-progress\n--discard-changes\" being used to quit merges or cherry-picks or\nreverts or even rebases.  In my opinion, doing so is creating flags to\ncombine uncommon pairs of git commands (git <operation> --quit + git\nswitch) in a way that is far less clear.  I think that's a bad route\nto go down, and we should keep the commands orthogonal (if I could\nstart all over, I'd also make reset and checkout and everything else\nstop modifying any in-progress state).\n\nInstead, I would either:\n\n  * Drop `--ignore-in-progress` for now.  (Although Duy had a\nmeaningful usecase)\n\nOR\n\n  * Make `git switch --ignore-in-progress <branch>` leave all process\nstate in place and switch branches, if we would otherwise be able to\nswitch branches (i.e. there isn't dirty or conflicted changes in the\nway).  BUT, make sure to also:\n  * Make '--ignore-in-progress' incompatible with both '-m' and\n'--discard-changes'; if folks try to use either of those additional\noptions with --ignore-in-progress, tell people to use `<operation>\n--quit` first.  Do NOT provide an override. (Alternatively, refer to\n`<operation> --quit` as the override, since it is).\n\n\nElijah\n"},{"id":"372656","messageId":"20190329103919.15642-1-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190321131655.15249-1-pclouds@gmail.com","subject":"[PATCH v6 00/27] Add new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:52Z","receivedAt":"2019-03-29T10:39:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This adds a new command 'git-switch' as the half-replacement for\n'git-checkout'. Jump to 13/27 as the starting point. The other half is\ngit-restore, which is dealt with separately.\n\nThe sixth time must be the charm, because the seventh time may be the\ncurse!\n\nThe last big change since v5 is from the discussion about\nremove_branch_state() and its relation with --ignore-in-progress. By\npopular demand, --ignore-in-progress is gone. The discussion is linked\nin 23/27, which is also improved to suggest a way out.\n\nThat discussion also leads to a new patch, 07/27, which improves 'git\ncheckout' and tells the user when merge/revert/cherry-pick is canceled.\n\nThe rest is just minor updates here and there. And I made sure the\nsign-off is back this time :D\n\nNguyễn Thái Ngọc Duy (27):\n  git-checkout.txt: spell out --no-option\n  git-checkout.txt: fix one syntax line\n  doc: document --overwrite-ignore\n  git-checkout.txt: fix monospace typeset\n  t: rename t2014-switch.sh to t2014-checkout-switch.sh\n  checkout: advice how to get out of detached HEAD mode\n  checkout: inform the user when removing branch state\n  checkout: keep most #include sorted\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split part of it to new command 'switch'\n  switch: better names for -b and -B\n  switch: add --discard-changes\n  switch: remove -l\n  switch: stop accepting pathspec\n  switch: reject \"do nothing\" case\n  switch: only allow explicit detached HEAD\n  switch: add short option for --detach\n  switch: implicit dwim, use --no-guess to disable it\n  switch: no worktree status unless real branch switch happens\n  switch: reject if some operation is in progress\n  switch: make --orphan switch to an empty tree\n  t: add tests for switch\n  completion: support switch\n  doc: promote \"git switch\"\n\n .gitignore                                    |   1 +\n Documentation/config/advice.txt               |  13 +-\n Documentation/config/branch.txt               |   4 +-\n Documentation/config/checkout.txt             |  17 +-\n Documentation/config/diff.txt                 |   3 +-\n Documentation/git-branch.txt                  |  12 +-\n Documentation/git-check-ref-format.txt        |   3 +-\n Documentation/git-checkout.txt                | 221 ++++---\n Documentation/git-format-patch.txt            |   2 +-\n Documentation/git-merge-base.txt              |   2 +-\n Documentation/git-merge.txt                   |   5 +\n Documentation/git-rebase.txt                  |   2 +-\n Documentation/git-remote.txt                  |   2 +-\n Documentation/git-rerere.txt                  |  10 +-\n Documentation/git-reset.txt                   |  20 +-\n Documentation/git-stash.txt                   |   9 +-\n Documentation/git-switch.txt (new)            | 271 ++++++++\n Documentation/gitattributes.txt               |   3 +-\n Documentation/gitcore-tutorial.txt            |  19 +-\n Documentation/giteveryday.txt                 |  24 +-\n Documentation/githooks.txt                    |   8 +-\n Documentation/gittutorial.txt                 |   4 +-\n Documentation/gitworkflows.txt                |   3 +-\n Documentation/revisions.txt                   |   2 +-\n Documentation/user-manual.txt                 |  56 +-\n Makefile                                      |   1 +\n advice.c                                      |  17 +-\n branch.c                                      |  11 +-\n branch.h                                      |   2 +-\n builtin.h                                     |   1 +\n builtin/am.c                                  |   2 +-\n builtin/checkout.c                            | 619 ++++++++++--------\n builtin/rebase.c                              |   4 +-\n builtin/reset.c                               |   2 +-\n builtin/revert.c                              |   2 +-\n command-list.txt                              |   1 +\n contrib/completion/git-completion.bash        |  37 +-\n git.c                                         |   1 +\n parse-options-cb.c                            |  17 +\n parse-options.h                               |   1 +\n sha1-name.c                                   |   2 +-\n t/t1090-sparse-checkout-scope.sh              |  14 -\n ...014-switch.sh => t2014-checkout-switch.sh} |   0\n t/t2020-checkout-detach.sh                    |  28 +-\n t/t2060-switch.sh (new +x)                    |  96 +++\n unpack-trees.c                                |   2 +-\n 46 files changed, 1071 insertions(+), 505 deletions(-)\n create mode 100644 Documentation/git-switch.txt\n rename t/{t2014-switch.sh => t2014-checkout-switch.sh} (100%)\n create mode 100755 t/t2060-switch.sh\n\nRange-diff dựa trên v5:\n -:  ---------- >  1:  ddcc6f7684 checkout: inform the user when removing branch state\n 1:  0bb2aefb85 =  2:  1d91c3ed35 checkout: keep most #include sorted\n 2:  ace82aa26f =  3:  1705d50f4e checkout: factor out some code in parse_branchname_arg()\n 3:  6e13efcba8 =  4:  dc07f2fd92 checkout: make \"opts\" in cmd_checkout() a pointer\n 4:  f61a042eb8 =  5:  a8292a7173 checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n 5:  972cebc568 =  6:  89b7d4baea checkout: split options[] array in three pieces\n 6:  970c727b24 !  7:  0862b680c3 checkout: split part of it to new command 'switch'\n    @@ -243,18 +243,14 @@\n     +\n     +-f::\n     +--force::\n    -+\tAn alias for `--discard-changes` and `--ignore-in-progress`.\n    ++\tAn alias for `--discard-changes`.\n     +\n     +--discard-changes::\n     +\tProceed even if the index or the working tree differs from\n     +\t`HEAD`. Both the index and working tree are restored to match\n    -+\tthe switching target. This is used to throw away local\n    -+\tchanges.\n    -+\n    -+--ignore-in-progress::\n    -+\t`git switch` by default refuses when some operation is in\n    -+\tprogress (e.g. \"git rebase\", \"git am\" ...). This option\n    -+\toverrides this safety check and allows switching.\n    ++\tthe switching target. If `--recurse-submodules` is specified,\n    ++\tsubmodule content is also restored to match the switching\n    ++\ttarget. This is used to throw away local changes.\n     +\n     +-m::\n     +--merge::\n    @@ -321,13 +317,12 @@\n     +\n     +--recurse-submodules::\n     +--no-recurse-submodules::\n    -+\tUsing `--recurse-submodules` will update the content of all initialized\n    -+\tsubmodules according to the commit recorded in the superproject. If\n    -+\tlocal modifications in a submodule would be overwritten the checkout\n    -+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n    -+\tis used, the work trees of submodules will not be updated.\n    -+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n    -+\tsubmodule.\n    ++\tUsing `--recurse-submodules` will update the content of all\n    ++\tinitialized submodules according to the commit recorded in the\n    ++\tsuperproject. If nothing (or `--no-recurse-submodules`) is\n    ++\tused, the work trees of submodules will not be updated. Just\n    ++\tlike linkgit:git-submodule[1], this will detach `HEAD` of the\n    ++\tsubmodules.\n     +\n     +EXAMPLES\n     +--------\n    @@ -385,7 +380,7 @@\n     +name:\n     +\n     +------------\n    -+$ git switch -g new-topic\n    ++$ git switch new-topic\n     +Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n     +Switched to a new branch 'new-topic'\n     +------------\n 7:  676f5df0fd =  8:  52da28d384 switch: better names for -b and -B\n 8:  4e37c0659e !  9:  471b0c47ac switch: add --discard-changes\n    @@ -7,6 +7,10 @@\n         --discard-changes. But it's meant to be an alias for potentially more\n         force options in the future.\n     \n    +    Side note. It's not obvious from the patch but --discard-changes also\n    +    affects submodules if --recurse-submodules is used. The knob to force\n    +    updating submodules is hidden behind unpack-trees.c\n    +\n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n      +++ b/builtin/checkout.c\n 9:  a57208d137 = 10:  bf6d12272c switch: remove -l\n10:  3de6f95bf2 = 11:  bec2fb8884 switch: stop accepting pathspec\n11:  ad225517cd = 12:  ed0dfccf89 switch: reject \"do nothing\" case\n12:  583cfd5cc4 = 13:  d17eabfb01 switch: only allow explicit detached HEAD\n13:  1c5aee658d = 14:  59623757ee switch: add short option for --detach\n14:  d942ac52e2 = 15:  04dca884f8 switch: implicit dwim, use --no-guess to disable it\n15:  37eb152c0d = 16:  c8ff76def6 switch: no worktree status unless real branch switch happens\n16:  50d6768afd ! 17:  34727e7d46 switch: reject if some operation is in progress\n    @@ -7,13 +7,20 @@\n         forget that you're in the middle of something. By the time you realize,\n         you may have done a ton of work and it gets harder to go back.\n     \n    -    The new option --ignore-in-progress is added to override this check.\n    -    However it's probably just safer to create a separate worktree for\n    -    that separate thing you want to work on and leave this worktree\n    -    alone (unless of course creating or preparing worktrees are not cheap).\n    +    A new option --ignore-in-progress was considered but dropped because it\n    +    was not exactly clear what should happen. Sometimes you can switch away\n    +    and get back safely and resume the operation. Sometimes not. And the\n    +    git-checkout behavior is automatically clear merge/revert/cherry-pick,\n    +    which makes it a bit even more confusing [1].\n     \n    -    --force is updated to also imply --ignore-in-progress because it is\n    -    supposed to be the \"just do your things and don't bother me\" option.\n    +    We may revisit and add this option in the future. But for now play it\n    +    safe and not allow it (you can't even skip this check with --force). The\n    +    user is suggested to cancel the operation by themselves (and hopefully\n    +    they do consider the consequences, not blindly type the command), or to\n    +    create a separate worktree instead of switching. The third option is\n    +    the good old \"git checkout\", but it's not mentioned.\n    +\n    +    [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n     \n      diff --git a/builtin/checkout.c b/builtin/checkout.c\n      --- a/builtin/checkout.c\n    @@ -35,44 +42,55 @@\n      \tconst char *new_branch;\n      \tconst char *new_branch_force;\n     @@\n    - \t    !new_branch_info->path)\n    - \t\tdie_expecting_a_branch(new_branch_info);\n    + \tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n    + }\n      \n    -+\tif (!opts->can_switch_when_in_progress) {\n    -+\t\tstruct wt_status_state state;\n    ++static void die_if_some_operation_in_progress(void)\n    ++{\n    ++\tstruct wt_status_state state;\n     +\n    -+\t\tmemset(&state, 0, sizeof(state));\n    -+\t\twt_status_get_state(the_repository, &state, 0);\n    ++\tmemset(&state, 0, sizeof(state));\n    ++\twt_status_get_state(the_repository, &state, 0);\n     +\n    -+\t\tif (state.merge_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch while merging\"));\n    -+\t\tif (state.am_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch in the middle of an am session\"));\n    -+\t\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch while rebasing\"));\n    -+\t\tif (state.cherry_pick_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch while cherry-picking\"));\n    -+\t\tif (state.revert_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch while reverting\"));\n    -+\t\tif (state.bisect_in_progress)\n    -+\t\t\tdie(_(\"cannot switch branch while bisecting\"));\n    -+\t}\n    ++\tif (state.merge_in_progress)\n    ++\t\tdie(_(\"cannot switch branch while merging\\n\"\n    ++\t\t      \"Consider \\\"git merge --quit\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++\tif (state.am_in_progress)\n    ++\t\tdie(_(\"cannot switch branch in the middle of an am session\\n\"\n    ++\t\t      \"Consider \\\"git am --quit\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n    ++\t\tdie(_(\"cannot switch branch while rebasing\\n\"\n    ++\t\t      \"Consider \\\"git rebase --quit\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++\tif (state.cherry_pick_in_progress)\n    ++\t\tdie(_(\"cannot switch branch while cherry-picking\\n\"\n    ++\t\t      \"Consider \\\"git cherry-pick --quit\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++\tif (state.revert_in_progress)\n    ++\t\tdie(_(\"cannot switch branch while reverting\\n\"\n    ++\t\t      \"Consider \\\"git revert --quit\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++\tif (state.bisect_in_progress)\n    ++\t\tdie(_(\"cannot switch branch while bisecting\\n\"\n    ++\t\t      \"Consider \\\"git bisect reset HEAD\\\" \"\n    ++\t\t      \"or \\\"git worktree add\\\".\"));\n    ++}\n    ++\n    + static int checkout_branch(struct checkout_opts *opts,\n    + \t\t\t   struct branch_info *new_branch_info)\n    + {\n    +@@\n    + \t    !new_branch_info->path)\n    + \t\tdie_expecting_a_branch(new_branch_info);\n    + \n    ++\tif (!opts->can_switch_when_in_progress)\n    ++\t\tdie_if_some_operation_in_progress();\n     +\n      \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n      \t    !opts->ignore_other_worktrees) {\n      \t\tint flag;\n    -@@\n    - \t\topts->merge = 1; /* implied */\n    - \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n    - \t}\n    --\tif (opts->force)\n    -+\tif (opts->force) {\n    - \t\topts->discard_changes = 1;\n    -+\t\topts->can_switch_when_in_progress = 1;\n    -+\t}\n    - \n    - \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n    - \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n     @@\n      \topts.only_merge_on_switching_branches = 0;\n      \topts.accept_pathspec = 1;\n    @@ -81,15 +99,6 @@\n      \n      \toptions = parse_options_dup(checkout_options);\n      \toptions = add_common_options(&opts, options);\n    -@@\n    - \t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n    - \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n    - \t\t\t N_(\"throw away local modifications\")),\n    -+\t\tOPT_BOOL(0, \"ignore-in-progress\", &opts.can_switch_when_in_progress,\n    -+\t\t\t N_(\"allow to switch branch when some operation is still in progress\")),\n    - \t\tOPT_END()\n    - \t};\n    - \tint ret;\n     @@\n      \topts.switch_branch_doing_nothing_is_ok = 0;\n      \topts.only_merge_on_switching_branches = 1;\n17:  af8bb710c8 = 18:  6e1986f0d4 switch: make --orphan switch to an empty tree\n18:  4a293a3d53 ! 19:  414a1783fb t: add tests for switch\n    @@ -95,13 +95,11 @@\n     +\ttest_cmp expected actual\n     +'\n     +\n    -+test_expect_success 'switching when something is in progress' '\n    ++test_expect_success 'not switching when something is in progress' '\n     +\ttest_when_finished rm -f .git/MERGE_HEAD &&\n     +\t# fake a merge-in-progress\n     +\tcp .git/HEAD .git/MERGE_HEAD &&\n    -+\ttest_must_fail git switch -d @^ &&\n    -+\tgit switch --ignore-in-progress -d @^ &&\n    -+\tgit switch -\n    ++\ttest_must_fail git switch -d @^\n     +'\n     +\n     +test_done\n19:  b10290b378 = 20:  ac6f5fa9f9 completion: support switch\n20:  67c5175e5f ! 21:  5a921aa0a1 doc: promote \"git switch\"\n    @@ -9,6 +9,9 @@\n         For these reasons, promote it everywhere possible. This includes\n         documentation, suggestions/advice from other commands...\n     \n    +    The \"Checking out files\" progress line in unpack-trees.c is also updated\n    +    to \"Updating files\" to be neutral to both git-checkout and git-switch.\n    +\n      diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n      --- a/Documentation/git-branch.txt\n      +++ b/Documentation/git-branch.txt\n    @@ -662,7 +665,8 @@\n     -\t\"  git checkout -b <new-branch-name>\\n\"\n     +\t\"  git switch -c <new-branch-name>\\n\"\n      \t\"\\n\"\n    - \t\"Or undo this checkout with:\\n\"\n    +-\t\"Or undo this checkout with:\\n\"\n    ++\t\"Or undo this operation with:\\n\"\n      \t\"\\n\"\n     -\t\"  git checkout -\\n\"\n     +\t\"  git switch -\\n\"\n    @@ -705,7 +709,8 @@\n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n      \n    - \tOr undo this checkout with:\n    +-\tOr undo this checkout with:\n    ++\tOr undo this operation with:\n      \n     -\t  git checkout -\n     +\t  git switch -\n    @@ -731,10 +736,24 @@\n     -\t  git checkout -b <new-branch-name>\n     +\t  git switch -c <new-branch-name>\n      \n    - \tOr undo this checkout with:\n    +-\tOr undo this checkout with:\n    ++\tOr undo this operation with:\n      \n     -\t  git checkout -\n     +\t  git switch -\n      \n      \tTurn off this advice by setting config variable advice.detachedHead to false\n      \n    +\n    + diff --git a/unpack-trees.c b/unpack-trees.c\n    + --- a/unpack-trees.c\n    + +++ b/unpack-trees.c\n    +@@\n    + \t\t\ttotal++;\n    + \t}\n    + \n    +-\treturn start_delayed_progress(_(\"Checking out files\"), total);\n    ++\treturn start_delayed_progress(_(\"Updating files\"), total);\n    + }\n    + \n    + static void setup_collided_checkout_detection(struct checkout *state,\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372657","messageId":"20190329103919.15642-2-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 01/27] git-checkout.txt: spell out --no-option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:53Z","receivedAt":"2019-03-29T10:39:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's easier to search for and also less cryptic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f179b43732..99c8c0dc0f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -118,7 +118,8 @@ OPTIONS\n --quiet::\n \tQuiet, suppress feedback messages.\n \n---[no-]progress::\n+--progress::\n+--no-progress::\n \tProgress status is reported on the standard error stream\n \tby default when it is attached to a terminal, unless `--quiet`\n \tis specified. This flag enables progress reporting even if not\n@@ -262,7 +263,7 @@ edits from your current working tree. See the ``Interactive Mode''\n section of linkgit:git-add[1] to learn how to operate the `--patch` mode.\n +\n Note that this option uses the no overlay mode by default (see also\n-`--[no-]overlay`), and currently doesn't support overlay mode.\n+`--overlay`), and currently doesn't support overlay mode.\n \n --ignore-other-worktrees::\n \t`git checkout` refuses when the wanted ref is already checked\n@@ -270,7 +271,8 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n---[no-]recurse-submodules::\n+--recurse-submodules::\n+--no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n@@ -283,7 +285,8 @@ Note that this option uses the no overlay mode by default (see also\n \tDo not attempt to create a branch if a remote tracking branch\n \tof the same name exists.\n \n---[no-]overlay::\n+--overlay::\n+--no-overlay::\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372658","messageId":"20190329103919.15642-3-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 02/27] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:54Z","receivedAt":"2019-03-29T10:39:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 99c8c0dc0f..28817cfa41 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372659","messageId":"20190329103919.15642-4-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 03/27] doc: document --overwrite-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:55Z","receivedAt":"2019-03-29T10:40:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I added this option in git-checkout and git-merge in c1d7036b6b\n(checkout,merge: disallow overwriting ignored files with\n--no-overwrite-ignore - 2011-11-27) but did not remember to update\ndocumentation. This completes that commit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 6 ++++++\n Documentation/git-merge.txt    | 5 +++++\n 2 files changed, 11 insertions(+)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 28817cfa41..5280d1f9ed 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -271,6 +271,12 @@ Note that this option uses the no overlay mode by default (see also\n \tout anyway. In other words, the ref can be held by more than one\n \tworktree.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files when switching branches. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort\n+\tthe operation when the new branch contains ignored files.\n+\n --recurse-submodules::\n --no-recurse-submodules::\n \tUsing --recurse-submodules will update the content of all initialized\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 4cc86469f3..6a9163d8fe 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -87,6 +87,11 @@ will be appended to the specified message.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--overwrite-ignore::\n+--no-overwrite-ignore::\n+\tSilently overwrite ignored files from the merge result. This\n+\tis the default behavior. Use `--no-overwrite-ignore` to abort.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372660","messageId":"20190329103919.15642-5-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 04/27] git-checkout.txt: fix monospace typeset","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:56Z","receivedAt":"2019-03-29T10:40:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Add backticks where we have none, replace single quotes with backticks\nand replace double-quotes. Drop double-quotes from nested constructions\nsuch as `\"@{-1}\"`.\n\nHelped-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 162 ++++++++++++++++-----------------\n 1 file changed, 81 insertions(+), 81 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 5280d1f9ed..bf90966c95 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -24,14 +24,14 @@ also update `HEAD` to set the specified branch as the current\n branch.\n \n 'git checkout' [<branch>]::\n-\tTo prepare for working on <branch>, switch to it by updating\n+\tTo prepare for working on `<branch>`, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n-\tHEAD at the branch. Local modifications to the files in the\n+\t`HEAD` at the branch. Local modifications to the files in the\n \tworking tree are kept, so that they can be committed to the\n-\t<branch>.\n+\t`<branch>`.\n +\n-If <branch> is not found but there does exist a tracking branch in\n-exactly one remote (call it <remote>) with a matching name, treat as\n+If `<branch>` is not found but there does exist a tracking branch in\n+exactly one remote (call it `<remote>`) with a matching name, treat as\n equivalent to\n +\n ------------\n@@ -47,7 +47,7 @@ branches from there if `<branch>` is ambiguous but exists on the\n 'origin' remote. See also `checkout.defaultRemote` in\n linkgit:git-config[1].\n +\n-You could omit <branch>, in which case the command degenerates to\n+You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n if exists, for the current branch.\n@@ -61,7 +61,7 @@ if exists, for the current branch.\n \t`--track` without `-b` implies branch creation; see the\n \tdescription of `--track` below.\n +\n-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it\n+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it\n is reset. This is the transactional equivalent of\n +\n ------------\n@@ -75,25 +75,25 @@ successful.\n 'git checkout' --detach [<branch>]::\n 'git checkout' [--detach] <commit>::\n \n-\tPrepare to work on top of <commit>, by detaching HEAD at it\n+\tPrepare to work on top of `<commit>`, by detaching `HEAD` at it\n \t(see \"DETACHED HEAD\" section), and updating the index and the\n \tfiles in the working tree.  Local modifications to the files\n \tin the working tree are kept, so that the resulting working\n \ttree will be the state recorded in the commit plus the local\n \tmodifications.\n +\n-When the <commit> argument is a branch name, the `--detach` option can\n-be used to detach HEAD at the tip of the branch (`git checkout\n-<branch>` would check out that branch without detaching HEAD).\n+When the `<commit>` argument is a branch name, the `--detach` option can\n+be used to detach `HEAD` at the tip of the branch (`git checkout\n+<branch>` would check out that branch without detaching `HEAD`).\n +\n-Omitting <branch> detaches HEAD at the tip of the current branch.\n+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.\n \n 'git checkout' [<tree-ish>] [--] <pathspec>...::\n \n \tOverwrite paths in the working tree by replacing with the\n-\tcontents in the index or in the <tree-ish> (most often a\n-\tcommit).  When a <tree-ish> is given, the paths that\n-\tmatch the <pathspec> are updated both in the index and in\n+\tcontents in the index or in the `<tree-ish>` (most often a\n+\tcommit).  When a `<tree-ish>` is given, the paths that\n+\tmatch the `<pathspec>` are updated both in the index and in\n \tthe working tree.\n +\n The index may contain unmerged entries because of a previous failed merge.\n@@ -128,7 +128,7 @@ OPTIONS\n -f::\n --force::\n \tWhen switching branches, proceed even if the index or the\n-\tworking tree differs from HEAD.  This is used to throw away\n+\tworking tree differs from `HEAD`.  This is used to throw away\n \tlocal changes.\n +\n When checking out paths from the index, do not fail upon unmerged\n@@ -155,12 +155,12 @@ on your side branch as `theirs` (i.e. \"one contributor's work on top\n of it\").\n \n -b <new_branch>::\n-\tCreate a new branch named <new_branch> and start it at\n-\t<start_point>; see linkgit:git-branch[1] for details.\n+\tCreate a new branch named `<new_branch>` and start it at\n+\t`<start_point>`; see linkgit:git-branch[1] for details.\n \n -B <new_branch>::\n-\tCreates the branch <new_branch> and start it at <start_point>;\n-\tif it already exists, then reset it to <start_point>. This is\n+\tCreates the branch `<new_branch>` and start it at `<start_point>`;\n+\tif it already exists, then reset it to `<start_point>`. This is\n \tequivalent to running \"git branch\" with \"-f\"; see\n \tlinkgit:git-branch[1] for details.\n \n@@ -173,15 +173,15 @@ If no `-b` option is given, the name of the new branch will be\n derived from the remote-tracking branch, by looking at the local part of\n the refspec configured for the corresponding remote, and then stripping\n the initial part up to the \"*\".\n-This would tell us to use \"hack\" as the local branch when branching\n-off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n-\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n+This would tell us to use `hack` as the local branch when branching\n+off of `origin/hack` (or `remotes/origin/hack`, or even\n+`refs/remotes/origin/hack`).  If the given name has no slash, or the above\n guessing results in an empty name, the guessing is aborted.  You can\n explicitly give a name with `-b` in such a case.\n \n --no-track::\n \tDo not set up \"upstream\" configuration, even if the\n-\tbranch.autoSetupMerge configuration variable is true.\n+\t`branch.autoSetupMerge` configuration variable is true.\n \n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n@@ -190,21 +190,21 @@ explicitly give a name with `-b` in such a case.\n --detach::\n \tRather than checking out a branch to work on it, check out a\n \tcommit for inspection and discardable experiments.\n-\tThis is the default behavior of \"git checkout <commit>\" when\n-\t<commit> is not a branch name.  See the \"DETACHED HEAD\" section\n+\tThis is the default behavior of `git checkout <commit>` when\n+\t`<commit>` is not a branch name.  See the \"DETACHED HEAD\" section\n \tbelow for details.\n \n --orphan <new_branch>::\n-\tCreate a new 'orphan' branch, named <new_branch>, started from\n-\t<start_point> and switch to it.  The first commit made on this\n+\tCreate a new 'orphan' branch, named `<new_branch>`, started from\n+\t`<start_point>` and switch to it.  The first commit made on this\n \tnew branch will have no parents and it will be the root of a new\n \thistory totally disconnected from all the other branches and\n \tcommits.\n +\n The index and the working tree are adjusted as if you had previously run\n-\"git checkout <start_point>\".  This allows you to start a new history\n-that records a set of paths similar to <start_point> by easily running\n-\"git commit -a\" to make the root commit.\n+`git checkout <start_point>`.  This allows you to start a new history\n+that records a set of paths similar to `<start_point>` by easily running\n+`git commit -a` to make the root commit.\n +\n This can be useful when you want to publish the tree from a commit\n without exposing its full history. You might want to do this to publish\n@@ -213,17 +213,17 @@ whose full history contains proprietary or otherwise encumbered bits of\n code.\n +\n If you want to start a disconnected history that records a set of paths\n-that is totally different from the one of <start_point>, then you should\n+that is totally different from the one of `<start_point>`, then you should\n clear the index and the working tree right after creating the orphan\n-branch by running \"git rm -rf .\" from the top level of the working tree.\n+branch by running `git rm -rf .` from the top level of the working tree.\n Afterwards you will be ready to prepare your new files, repopulating the\n working tree, by copying them from elsewhere, extracting a tarball, etc.\n \n --ignore-skip-worktree-bits::\n \tIn sparse checkout mode, `git checkout -- <paths>` would\n-\tupdate only entries matched by <paths> and sparse patterns\n-\tin $GIT_DIR/info/sparse-checkout. This option ignores\n-\tthe sparse patterns and adds back any files in <paths>.\n+\tupdate only entries matched by `<paths>` and sparse patterns\n+\tin `$GIT_DIR/info/sparse-checkout`. This option ignores\n+\tthe sparse patterns and adds back any files in `<paths>`.\n \n -m::\n --merge::\n@@ -245,18 +245,18 @@ When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n \n --conflict=<style>::\n-\tThe same as --merge option above, but changes the way the\n+\tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n-\tmerge.conflictStyle configuration variable.  Possible values are\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n \t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n \t\"merge\" style, shows the original contents).\n \n -p::\n --patch::\n \tInteractively select hunks in the difference between the\n-\t<tree-ish> (or the index, if unspecified) and the working\n+\t`<tree-ish>` (or the index, if unspecified) and the working\n \ttree.  The chosen hunks are then applied in reverse to the\n-\tworking tree (and if a <tree-ish> was specified, the index).\n+\tworking tree (and if a `<tree-ish>` was specified, the index).\n +\n This means that you can use `git checkout -p` to selectively discard\n edits from your current working tree. See the ``Interactive Mode''\n@@ -279,13 +279,13 @@ Note that this option uses the no overlay mode by default (see also\n \n --recurse-submodules::\n --no-recurse-submodules::\n-\tUsing --recurse-submodules will update the content of all initialized\n+\tUsing `--recurse-submodules` will update the content of all initialized\n \tsubmodules according to the commit recorded in the superproject. If\n \tlocal modifications in a submodule would be overwritten the checkout\n-\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\twill fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)\n \tis used, the work trees of submodules will not be updated.\n-\tJust like linkgit:git-submodule[1], this will detach the\n-\tsubmodules HEAD.\n+\tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodule.\n \n --no-guess::\n \tDo not attempt to create a branch if a remote tracking branch\n@@ -296,21 +296,21 @@ Note that this option uses the no overlay mode by default (see also\n \tIn the default overlay mode, `git checkout` never\n \tremoves files from the index or the working tree.  When\n \tspecifying `--no-overlay`, files that appear in the index and\n-\tworking tree, but not in <tree-ish> are removed, to make them\n-\tmatch <tree-ish> exactly.\n+\tworking tree, but not in `<tree-ish>` are removed, to make them\n+\tmatch `<tree-ish>` exactly.\n \n <branch>::\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n \twhen prepended with \"refs/heads/\", is a valid ref), then that\n \tbranch is checked out. Otherwise, if it refers to a valid\n-\tcommit, your HEAD becomes \"detached\" and you are no longer on\n+\tcommit, your `HEAD` becomes \"detached\" and you are no longer on\n \tany branch (see below for details).\n +\n-You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+You can use the `@{-N}` syntax to refer to the N-th last\n branch/commit checked out using \"git checkout\" operation. You may\n-also specify `-` which is synonymous to `\"@{-1}\"`.\n+also specify `-` which is synonymous to `@{-1}`.\n +\n-As a special case, you may use `\"A...B\"` as a shortcut for the\n+As a special case, you may use `A...B` as a shortcut for the\n merge base of `A` and `B` if there is exactly one merge base. You can\n leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n@@ -319,7 +319,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n <start_point>::\n \tThe name of a commit at which to start the new branch; see\n-\tlinkgit:git-branch[1] for details. Defaults to HEAD.\n+\tlinkgit:git-branch[1] for details. Defaults to `HEAD`.\n \n <tree-ish>::\n \tTree to checkout from (when paths are given). If not specified,\n@@ -329,9 +329,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n DETACHED HEAD\n -------------\n-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each\n branch refers to a specific commit. Let's look at a repo with three\n-commits, one of them tagged, and with branch 'master' checked out:\n+commits, one of them tagged, and with branch `master` checked out:\n \n ------------\n            HEAD (refers to branch 'master')\n@@ -344,10 +344,10 @@ a---b---c  branch 'master' (refers to commit 'c')\n ------------\n \n When a commit is created in this state, the branch is updated to refer to\n-the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n-parent is commit 'c', and then updates branch 'master' to refer to new\n-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n-to commit 'd':\n+the new commit. Specifically, 'git commit' creates a new commit `d`, whose\n+parent is commit `c`, and then updates branch `master` to refer to new\n+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers\n+to commit `d`:\n \n ------------\n $ edit; git add; git commit\n@@ -364,7 +364,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n It is sometimes useful to be able to checkout a commit that is not at\n the tip of any named branch, or even to create a new commit that is not\n referenced by a named branch. Let's look at what happens when we\n-checkout commit 'b' (here we show two ways this may be done):\n+checkout commit `b` (here we show two ways this may be done):\n \n ------------\n $ git checkout v2.0  # or\n@@ -379,9 +379,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-Notice that regardless of which checkout command we use, HEAD now refers\n-directly to commit 'b'. This is known as being in detached HEAD state.\n-It means simply that HEAD refers to a specific commit, as opposed to\n+Notice that regardless of which checkout command we use, `HEAD` now refers\n+directly to commit `b`. This is known as being in detached `HEAD` state.\n+It means simply that `HEAD` refers to a specific commit, as opposed to\n referring to a named branch. Let's see what happens when we create a commit:\n \n ------------\n@@ -398,7 +398,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n   tag 'v2.0' (refers to commit 'b')\n ------------\n \n-There is now a new commit 'e', but it is referenced only by HEAD. We can\n+There is now a new commit `e`, but it is referenced only by `HEAD`. We can\n of course add yet another commit in this state:\n \n ------------\n@@ -416,7 +416,7 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n In fact, we can perform all the normal Git operations. But, let's look\n-at what happens when we then checkout master:\n+at what happens when we then checkout `master`:\n \n ------------\n $ git checkout master\n@@ -431,9 +431,9 @@ a---b---c---d  branch 'master' (refers to commit 'd')\n ------------\n \n It is important to realize that at this point nothing refers to commit\n-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted\n by the routine Git garbage collection process, unless we create a reference\n-before that happens. If we have not yet moved away from commit 'f',\n+before that happens. If we have not yet moved away from commit `f`,\n any of these will create a reference to it:\n \n ------------\n@@ -442,19 +442,19 @@ $ git branch foo        <2>\n $ git tag foo           <3>\n ------------\n \n-<1> creates a new branch 'foo', which refers to commit 'f', and then\n-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n-    be in detached HEAD state after this command.\n+<1> creates a new branch `foo`, which refers to commit `f`, and then\n+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer\n+    be in detached `HEAD` state after this command.\n \n-<2> similarly creates a new branch 'foo', which refers to commit 'f',\n-    but leaves HEAD detached.\n+<2> similarly creates a new branch `foo`, which refers to commit `f`,\n+    but leaves `HEAD` detached.\n \n-<3> creates a new tag 'foo', which refers to commit 'f',\n-    leaving HEAD detached.\n+<3> creates a new tag `foo`, which refers to commit `f`,\n+    leaving `HEAD` detached.\n \n-If we have moved away from commit 'f', then we must first recover its object\n+If we have moved away from commit `f`, then we must first recover its object\n name (typically by using git reflog), and then we can create a reference to\n-it. For example, to see the last two commits to which HEAD referred, we\n+it. For example, to see the last two commits to which `HEAD` referred, we\n can use either of these commands:\n \n ------------\n@@ -465,12 +465,12 @@ $ git log -g -2 HEAD\n ARGUMENT DISAMBIGUATION\n -----------------------\n \n-When there is only one argument given and it is not `--` (e.g. \"git\n-checkout abc\"), and when the argument is both a valid `<tree-ish>`\n-(e.g. a branch \"abc\" exists) and a valid `<pathspec>` (e.g. a file\n+When there is only one argument given and it is not `--` (e.g. `git\n+checkout abc`), and when the argument is both a valid `<tree-ish>`\n+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file\n or a directory whose name is \"abc\" exists), Git would usually ask\n you to disambiguate.  Because checking out a branch is so common an\n-operation, however, \"git checkout abc\" takes \"abc\" as a `<tree-ish>`\n+operation, however, `git checkout abc` takes \"abc\" as a `<tree-ish>`\n in such a situation.  Use `git checkout -- <pathspec>` if you want\n to checkout these paths out of the index.\n \n@@ -478,7 +478,7 @@ EXAMPLES\n --------\n \n . The following sequence checks out the `master` branch, reverts\n-  the `Makefile` to two revisions back, deletes hello.c by\n+  the `Makefile` to two revisions back, deletes `hello.c` by\n   mistake, and gets it back from the index.\n +\n ------------\n@@ -490,7 +490,7 @@ $ git checkout hello.c            <3>\n +\n <1> switch branch\n <2> take a file out of another commit\n-<3> restore hello.c from the index\n+<3> restore `hello.c` from the index\n +\n If you want to check out _all_ C source files out of the index,\n you can say\n@@ -519,7 +519,7 @@ $ git checkout -- hello.c\n $ git checkout mytopic\n ------------\n +\n-However, your \"wrong\" branch and correct \"mytopic\" branch may\n+However, your \"wrong\" branch and correct `mytopic` branch may\n differ in files that you have modified locally, in which case\n the above checkout would fail like this:\n +\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372661","messageId":"20190329103919.15642-6-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 05/27] t: rename t2014-switch.sh to t2014-checkout-switch.sh","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:57Z","receivedAt":"2019-03-29T10:40:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The old name does not really say that this is about 'checkout -b'. See\n49d833dc07 (Revert \"checkout branch: prime cache-tree fully\" -\n2009-05-12) for more information\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/{t2014-switch.sh => t2014-checkout-switch.sh} | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh\nsimilarity index 100%\nrename from t/t2014-switch.sh\nrename to t/t2014-checkout-switch.sh\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372662","messageId":"20190329103919.15642-7-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 06/27] checkout: advice how to get out of detached HEAD mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:58Z","receivedAt":"2019-03-29T10:40:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Detached HEAD mode is considered dangerous and confusing for newcomers\nand we print a big block of warning how to move forward. But we should\nalso suggest the user the way to get out of it if they get into detached\nHEAD by mistake.\n\nWhile at there, I also suggest how to turn the advice off. This is\nanother thing I find annoying with advices and should be dealt with in a\nmore generic way. But that may require some refactoring in advice.c\nfirst.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c                   | 17 +++++++++++++----\n t/t2020-checkout-detach.sh | 12 ++++++++++++\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex 567209aa79..b224825637 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,13 +191,22 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: checking out '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"\\n\"\n+\t\"Or undo this checkout with:\\n\"\n+\t\"\\n\"\n+\t\"  git checkout -\\n\"\n+\t\"\\n\"\n+\t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 1fa670625c..743c7248a2 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -206,6 +206,12 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit three\n \tEOF\n \n@@ -282,6 +288,12 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t  git checkout -b <new-branch-name>\n \n+\tOr undo this checkout with:\n+\n+\t  git checkout -\n+\n+\tTurn off this advice by setting config variable advice.detachedHead to false\n+\n \tHEAD is now at \\$commit... three\n \tEOF\n \n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372663","messageId":"20190329103919.15642-8-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:38:59Z","receivedAt":"2019-03-29T10:40:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After a successful switch, if a merge, cherry-pick or revert is ongoing,\nit is canceled. This behavior has been with us from the very early\nbeginning, soon after git-merge was created but never actually\ndocumented [1]. It may be a good idea to be transparent and tell the\nuser if some operation is canceled.\n\nI consider this a better way of telling the user than just adding a\nsentence or two in git-checkout.txt, which will be mostly ignored\nanyway.\n\nPS. Originally I wanted to print more details like\n\n    warning: cancelling an in-progress merge from <SHA-1>\n\nwhich may allow some level of undo if the user wants to. But that seems\na lot more work. Perhaps it can be improved later if people still want\nthat.\n\n[1] ... and I will try not to argue whether it is a sensible behavior.\nThere is some more discussion here if people are interested:\nCACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n branch.c           | 11 +++++++----\n branch.h           |  2 +-\n builtin/am.c       |  2 +-\n builtin/checkout.c |  2 +-\n builtin/rebase.c   |  4 ++--\n builtin/reset.c    |  2 +-\n builtin/revert.c   |  2 +-\n 7 files changed, 14 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 28b81a7e02..8dd5bb9f1c 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -337,11 +337,14 @@ void create_branch(struct repository *r,\n \tfree(real_ref);\n }\n \n-void remove_branch_state(struct repository *r)\n+void remove_branch_state(struct repository *r, int verbose)\n {\n-\tunlink(git_path_cherry_pick_head(r));\n-\tunlink(git_path_revert_head(r));\n-\tunlink(git_path_merge_head(r));\n+\tif (!unlink(git_path_cherry_pick_head(r)) && verbose)\n+\t\twarning(_(\"cancelling a cherry picking in progress\"));\n+\tif (!unlink(git_path_revert_head(r)) && verbose)\n+\t\twarning(_(\"cancelling a revert in progress\"));\n+\tif (!unlink(git_path_merge_head(r)) && verbose)\n+\t\twarning(_(\"cancelling a merge in progress\"));\n \tunlink(git_path_merge_rr(r));\n \tunlink(git_path_merge_msg(r));\n \tunlink(git_path_merge_mode(r));\ndiff --git a/branch.h b/branch.h\nindex 29c1afa4d0..aed045901e 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -64,7 +64,7 @@ extern int validate_new_branchname(const char *name, struct strbuf *ref, int for\n  * Remove information about the state of working on the current\n  * branch. (E.g., MERGE_HEAD)\n  */\n-void remove_branch_state(struct repository *r);\n+void remove_branch_state(struct repository *r, int verbose);\n \n /*\n  * Configure local branch \"local\" as downstream to branch \"remote\"\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 4fb107a9d1..99b66508fd 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1957,7 +1957,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem\n \tif (merge_tree(remote_tree))\n \t\treturn -1;\n \n-\tremove_branch_state(the_repository);\n+\tremove_branch_state(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e6037b296..f66bd2f56d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -899,7 +899,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\t\t\tdelete_reflog(old_branch_info->path);\n \t\t}\n \t}\n-\tremove_branch_state(the_repository);\n+\tremove_branch_state(the_repository, !opts->quiet);\n \tstrbuf_release(&msg);\n \tif (!opts->quiet &&\n \t    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, \"HEAD\"))))\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 52114cbf0d..646d0f9fb1 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1272,7 +1272,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n \t\t\t       NULL, NULL) < 0)\n \t\t\tdie(_(\"could not discard worktree changes\"));\n-\t\tremove_branch_state(the_repository);\n+\t\tremove_branch_state(the_repository, 0);\n \t\tif (read_basic_state(&options))\n \t\t\texit(1);\n \t\tgoto run_rebase;\n@@ -1292,7 +1292,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t       NULL, NULL) < 0)\n \t\t\tdie(_(\"could not move back to %s\"),\n \t\t\t    oid_to_hex(&options.orig_head));\n-\t\tremove_branch_state(the_repository);\n+\t\tremove_branch_state(the_repository, 0);\n \t\tret = finish_rebase(&options);\n \t\tgoto cleanup;\n \t}\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 7882829a95..6d9397c844 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -420,7 +420,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n \t}\n \tif (!pathspec.nr)\n-\t\tremove_branch_state(the_repository);\n+\t\tremove_branch_state(the_repository, 0);\n \n \treturn update_ref_status;\n }\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a47b53ceaf..ebf2789225 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -196,7 +196,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tif (cmd == 'q') {\n \t\tint ret = sequencer_remove_state(opts);\n \t\tif (!ret)\n-\t\t\tremove_branch_state(the_repository);\n+\t\t\tremove_branch_state(the_repository, 0);\n \t\treturn ret;\n \t}\n \tif (cmd == 'c')\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372664","messageId":"20190329103919.15642-9-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 08/27] checkout: keep most #include sorted","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:00Z","receivedAt":"2019-03-29T10:40:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The include list becomes very long and frankly a bit unorganized. With\nthe exception of builtin.h, cache.h or git-compat-util.h which have to\ncome first, keep the rest sorted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f66bd2f56d..15ee5181eb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1,30 +1,30 @@\n #define USE_THE_INDEX_COMPATIBILITY_MACROS\n #include \"builtin.h\"\n-#include \"config.h\"\n+#include \"advice.h\"\n+#include \"blob.h\"\n+#include \"branch.h\"\n+#include \"cache-tree.h\"\n #include \"checkout.h\"\n+#include \"commit.h\"\n+#include \"config.h\"\n+#include \"diff.h\"\n+#include \"dir.h\"\n+#include \"ll-merge.h\"\n #include \"lockfile.h\"\n+#include \"merge-recursive.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n-#include \"object-store.h\"\n-#include \"commit.h\"\n+#include \"remote.h\"\n+#include \"resolve-undo.h\"\n+#include \"revision.h\"\n+#include \"run-command.h\"\n+#include \"submodule.h\"\n+#include \"submodule-config.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n-#include \"dir.h\"\n-#include \"run-command.h\"\n-#include \"merge-recursive.h\"\n-#include \"branch.h\"\n-#include \"diff.h\"\n-#include \"revision.h\"\n-#include \"remote.h\"\n-#include \"blob.h\"\n #include \"xdiff-interface.h\"\n-#include \"ll-merge.h\"\n-#include \"resolve-undo.h\"\n-#include \"submodule-config.h\"\n-#include \"submodule.h\"\n-#include \"advice.h\"\n \n static int checkout_optimize_new_branch;\n \n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372665","messageId":"20190329103919.15642-10-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 09/27] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:01Z","receivedAt":"2019-03-29T10:40:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 15ee5181eb..897eefe16d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1081,6 +1081,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -1088,10 +1116,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1213,26 +1239,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372666","messageId":"20190329103919.15642-11-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 10/27] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:02Z","receivedAt":"2019-03-29T10:40:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 115 +++++++++++++++++++++++----------------------\n 1 file changed, 58 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 897eefe16d..b6de8401fe 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1341,82 +1341,83 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n-\topts.overlay_mode = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n+\topts->overlay_mode = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !no_dwim_new_local_branch;\n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n-\tif (opts.overlay_mode == 1 && opts.patch_mode)\n+\tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"-p and --overlay are mutually exclusive\"));\n \n \t/*\n@@ -1424,14 +1425,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1440,7 +1441,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1459,56 +1460,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1527,6 +1528,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372667","messageId":"20190329103919.15642-12-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 11/27] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:03Z","receivedAt":"2019-03-29T10:40:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b6de8401fe..93fc2a5815 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -47,6 +47,8 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n+\tint no_dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -58,6 +60,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1344,8 +1347,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch, no_dwim_new_local_branch = 0;\n+\tint dwim_new_local_branch;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1370,12 +1372,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &no_dwim_new_local_branch,\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n \t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1393,6 +1395,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n+\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1401,7 +1404,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !no_dwim_new_local_branch;\n+\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1409,9 +1412,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372668","messageId":"20190329103919.15642-13-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 12/27] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:04Z","receivedAt":"2019-03-29T10:41:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 82 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 17 ++++++++++\n parse-options.h    |  1 +\n 3 files changed, 77 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 93fc2a5815..c15dc91e16 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1342,15 +1342,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1360,34 +1376,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n+\t\t\t   N_(\"update ignored files (default)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"do not second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts->overlay_mode, N_(\"use overlay mode (default)\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tint dwim_new_local_branch;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1401,6 +1432,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 2733393546..caaeed896f 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -122,6 +122,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\tmemset(opts + nr, 0, sizeof(*opts));\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..9a90c332a5 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -257,6 +257,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n+struct option *parse_options_dup(const struct option *a);\n struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372669","messageId":"20190329103919.15642-14-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 13/27] checkout: split part of it to new command 'switch'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:05Z","receivedAt":"2019-03-29T10:41:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To remedy that, the\ncommand will be split into two new ones: switch and restore. The good\nold \"git checkout\" command is still here and will be until all (or most\nof users) are sick of it.\n\nSee the new man page for the final design of switch. The actual\nimplementation though is still pretty much the same as \"git checkout\"\nand not completely aligned with the man page. Following patches will\nadjust their behavior to match the man page.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                         |   1 +\n Documentation/config/advice.txt    |  13 +-\n Documentation/config/branch.txt    |   4 +-\n Documentation/config/checkout.txt  |   9 +-\n Documentation/config/diff.txt      |   3 +-\n Documentation/git-checkout.txt     |   4 +\n Documentation/git-switch.txt (new) | 271 +++++++++++++++++++++++++++++\n Documentation/gitattributes.txt    |   3 +-\n Documentation/githooks.txt         |   8 +-\n Makefile                           |   1 +\n builtin.h                          |   1 +\n builtin/checkout.c                 |  60 +++++--\n command-list.txt                   |   1 +\n git.c                              |   1 +\n 14 files changed, 353 insertions(+), 27 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 7374587f9d..c687b92b1c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -167,6 +167,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 88620429ea..239d479506 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -42,7 +42,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -62,12 +63,14 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n-\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tlinkgit:git-checkout[1] and linkgit:git-switch[1]\n+\t\tambiguously resolves to a\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..8050466159 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -1,5 +1,5 @@\n branch.autoSetupMerge::\n-\tTells 'git branch' and 'git checkout' to set up new branches\n+\tTells 'git branch', 'git switch' and 'git checkout' to set up new branches\n \tso that linkgit:git-pull[1] will appropriately merge from the\n \tstarting point branch. Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n@@ -11,7 +11,7 @@ branch.autoSetupMerge::\n \tbranch. This option defaults to true.\n \n branch.autoSetupRebase::\n-\tWhen a new branch is created with 'git branch' or 'git checkout'\n+\tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n \tthat tracks another branch, this variable tells Git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n \tWhen `never`, rebase is never automatically set to true.\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex c4118fa196..d6872ffa83 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -1,5 +1,6 @@\n checkout.defaultRemote::\n-\tWhen you run 'git checkout <something>' and only have one\n+\tWhen you run 'git checkout <something>'\n+\tor 'git switch <something>' and only have one\n \tremote, it may implicitly fall back on checking out and\n \ttracking e.g. 'origin/<something>'. This stops working as soon\n \tas you have more than one remote with a '<something>'\n@@ -8,8 +9,10 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+or 'git switch <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex e48bb987d7..b3b304ee12 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -78,7 +78,8 @@ diff.external::\n diff.ignoreSubmodules::\n \tSets the default value of --ignore-submodules. Note that this\n \taffects only 'git diff' Porcelain, and not lower level 'diff'\n-\tcommands such as 'git diff-files'. 'git checkout' also honors\n+\tcommands such as 'git diff-files'. 'git checkout'\n+\tand 'git switch' also honor\n \tthis setting when reporting uncommitted changes. Setting it to\n \t'all' disables the submodule summary normally shown by 'git commit'\n \tand 'git status' when `status.submoduleSummary` is set unless it is\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex bf90966c95..c7192bdefe 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -560,6 +560,10 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nnew file mode 100644\nindex 0000000000..f15e5bdcf4\n--- /dev/null\n+++ b/Documentation/git-switch.txt\n@@ -0,0 +1,271 @@\n+git-switch(1)\n+=============\n+\n+NAME\n+----\n+git-switch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch' [<options>] [--no-guess] <branch>\n+'git switch' [<options>] --detach [<start-point>]\n+'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]\n+'git switch' [<options>] --orphan <new-branch>\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch. The working tree and the index are\n+updated to match the branch. All new commits will be added to the tip\n+of this branch.\n+\n+Optionally a new branch could be created with either `-c`, `-C`,\n+automatically from a remote branch of same name (see `--guess`), or\n+detach the working tree from any branch with `--detach`, along with\n+switching.\n+\n+Switching branches does not require a clean index and working tree\n+(i.e. no differences compared to `HEAD`). The operation is aborted\n+however if the operation leads to loss of local changes, unless told\n+otherwise with `--discard-changes` or `--merge`.\n+\n+OPTIONS\n+-------\n+<branch>::\n+\tBranch to switch to.\n+\n+<new-branch>::\n+\tName for the new branch.\n+\n+<start-point>::\n+\tThe starting point for the new branch. Specifying a\n+\t`<start-point>` allows you to create a branch based on some\n+\tother point in history than where HEAD currently points. (Or,\n+\tin the case of `--detach`, allows you to inspect and detach\n+\tfrom some other point.)\n++\n+You can use the `@{-N}` syntax to refer to the N-th last\n+branch/commit switched to using \"git switch\" or \"git checkout\"\n+operation. You may also specify `-` which is synonymous to `@{-1}`.\n+This is often used to switch quickly between two branches, or to undo\n+a branch switch by mistake.\n++\n+As a special case, you may use `A...B` as a shortcut for the merge\n+base of `A` and `B` if there is exactly one merge base. You can leave\n+out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+-c <new-branch>::\n+--create <new-branch>::\n+\tCreate a new branch named `<new-branch>` starting at\n+\t`<start-point>` before switching to the branch. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-C <new-branch>::\n+--force-create <new-branch>::\n+\tSimilar to `--create` except that if `<new-branch>` already\n+\texists, it will be reset to `<start-point>`. This is a\n+\tconvenient shortcut for:\n++\n+------------\n+$ git branch -f <new-branch>\n+$ git switch <new-branch>\n+------------\n+\n+-d::\n+--detach::\n+\tSwitch to a commit for inspection and discardable\n+\texperiments. See the \"DETACHED HEAD\" section in\n+\tlinkgit:git-checkout[1] for details.\n+\n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git switch -c <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`\n+to always checkout remote branches from there if `<branch>` is\n+ambiguous but exists on the 'origin' remote. See also\n+`checkout.defaultRemote` in linkgit:git-config[1].\n++\n+`--guess` is the default behavior. Use `--no-guess` to disable it.\n+\n+-f::\n+--force::\n+\tAn alias for `--discard-changes`.\n+\n+--discard-changes::\n+\tProceed even if the index or the working tree differs from\n+\t`HEAD`. Both the index and working tree are restored to match\n+\tthe switching target. If `--recurse-submodules` is specified,\n+\tsubmodule content is also restored to match the switching\n+\ttarget. This is used to throw away local changes.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as `--merge` option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\t`merge.conflictStyle` configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--progress::\n+--no-progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration.\n+\t`-c` is implied. See `--track` in linkgit:git-branch[1] for\n+\tdetails.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".  This would tell us to use `hack` as\n+the local branch when branching off of `origin/hack` (or\n+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the\n+given name has no slash, or the above guessing results in an empty\n+name, the guessing is aborted.  You can explicitly give a name with\n+`-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\t`branch.autoSetupMerge` configuration variable is true.\n+\n+--orphan <new-branch>::\n+\tCreate a new 'orphan' branch, named `<new-branch>`. All\n+\ttracked files are removed.\n+\n+--ignore-other-worktrees::\n+\t`git switch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--recurse-submodules::\n+--no-recurse-submodules::\n+\tUsing `--recurse-submodules` will update the content of all\n+\tinitialized submodules according to the commit recorded in the\n+\tsuperproject. If nothing (or `--no-recurse-submodules`) is\n+\tused, the work trees of submodules will not be updated. Just\n+\tlike linkgit:git-submodule[1], this will detach `HEAD` of the\n+\tsubmodules.\n+\n+EXAMPLES\n+--------\n+\n+The following command switches to the \"master\" branch:\n+\n+------------\n+$ git switch master\n+------------\n+\n+After working in the wrong branch, switching to the correct branch\n+would be done using:\n+\n+------------\n+$ git switch mytopic\n+------------\n+\n+However, your \"wrong\" branch and correct \"mytopic\" branch may differ\n+in files that you have modified locally, in which case the above\n+switch would fail like this:\n+\n+------------\n+$ git switch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n+\n+You can give the `-m` flag to the command, which would try a three-way\n+merge:\n+\n+------------\n+$ git switch -m mytopic\n+Auto-merging frotz\n+------------\n+\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+To switch back to the previous branch before we switched to mytopic\n+(i.e. \"master\" branch):\n+\n+------------\n+$ git switch -\n+------------\n+\n+You can grow a new branch from any commit. For example, switch to\n+\"HEAD~3\" and create branch \"fixup\":\n+\n+------------\n+$ git switch -c fixup HEAD~3\n+Switched to a new branch 'fixup'\n+------------\n+\n+If you want to start a new branch from a remote branch of the same\n+name:\n+\n+------------\n+$ git switch new-topic\n+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'\n+Switched to a new branch 'new-topic'\n+------------\n+\n+To check out commit `HEAD~3` for temporary inspection or experiment\n+without creating a new branch:\n+\n+------------\n+$ git switch --detach HEAD~3\n+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'\n+------------\n+\n+If it turns out whatever you have done is worth keeping, you can\n+always create a new name for it (without switching away):\n+\n+------------\n+$ git switch -c good-surprises\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1],\n+linkgit:git-branch[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 9b41f81c06..cd0f9fa507 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,8 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch', 'git checkout'  and 'git merge' run.\n+They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 5bf653c111..8ff72f0613 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.\n post-checkout\n ~~~~~~~~~~~~~\n \n-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the\n+This hook is invoked when a linkgit:git-checkout[1] or\n+linkgit:git-switch[1] is run after having updated the\n worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -406,7 +407,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Makefile b/Makefile\nindex 148668368b..8e91db73ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -802,6 +802,7 @@ BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..c64e44450e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -227,6 +227,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c15dc91e16..4989ca73a3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -34,6 +34,11 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1411,33 +1416,25 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n \tint dwim_new_local_branch;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n \topts->overlay_mode = -1;\n-\topts->no_dwim_new_local_branch = 0;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n@@ -1570,3 +1567,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.no_dwim_new_local_branch = 0;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..13317f47d4 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -171,6 +171,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch                              mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 2014aab6b8..39582cf511 100644\n--- a/git.c\n+++ b/git.c\n@@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch\", cmd_switch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372670","messageId":"20190329103919.15642-15-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 14/27] switch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:06Z","receivedAt":"2019-03-29T10:41:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch. And their descriptions are also tied to checkout. Move -b/-B\nto cmd_checkout() and new -c/-C with the same functionality in\ncmd_switch_branch()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 32 +++++++++++++++++++++-----------\n 1 file changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4989ca73a3..319ba372e3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1368,14 +1368,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1571,15 +1567,22 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1592,14 +1595,21 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372671","messageId":"20190329103919.15642-16-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 15/27] switch: add --discard-changes","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:07Z","receivedAt":"2019-03-29T10:41:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"--discard-changes is a better name than --force for this option since\nit's what really happens. --force is turned to an alias for\n--discard-changes. But it's meant to be an alias for potentially more\nforce options in the future.\n\nSide note. It's not obvious from the patch but --discard-changes also\naffects submodules if --recurse-submodules is used. The knob to force\nupdating submodules is hidden behind unpack-trees.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 12 ++++++++++--\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 319ba372e3..6d0b2ef565 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,6 +53,7 @@ struct checkout_opts {\n \tint count_checkout_paths;\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n+\tint discard_changes;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n-\tif (opts->force) {\n+\tif (opts->discard_changes) {\n \t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n \t\t\t\t opts, 1, writeout_error);\n \t\tif (ret)\n@@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->force && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \tif (opts->force && opts->merge)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-f\", \"-m\");\n \n+\tif (opts->discard_changes && opts->merge)\n+\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--discard-changes\", \"--merge\");\n+\n \tif (opts->force_detach && opts->new_branch)\n \t\tdie(_(\"'%s' cannot be used with '%s'\"),\n \t\t    \"--detach\", \"-b/-B/--orphan\");\n@@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n+\tif (opts->force)\n+\t\topts->discard_changes = 1;\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n@@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n+\t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372672","messageId":"20190329103919.15642-17-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 16/27] switch: remove -l","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:08Z","receivedAt":"2019-03-29T10:41:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This option is ancient. Nowadays reflog is enabled by default and\nautomatically created for new branches. Keep it in git-checkout only.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 6d0b2ef565..d63116b686 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1376,7 +1376,6 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -1579,6 +1578,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372673","messageId":"20190329103919.15642-18-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 17/27] switch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:09Z","receivedAt":"2019-03-29T10:41:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore\") of\ncourse do accept pathspec as before.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d63116b686..0584dc484d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -54,6 +54,7 @@ struct checkout_opts {\n \tint overlay_mode;\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1176,10 +1177,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1213,11 +1220,12 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\t/*\n-\t\t * Accept \"git checkout foo\" and \"git checkout foo --\"\n-\t\t * as candidates for dwim.\n+\t\t * Accept \"git checkout foo\", \"git checkout foo --\"\n+\t\t * and \"git switch foo\" as candidates for dwim.\n \t\t */\n \t\tif (!(argc == 1 && !has_dash_dash) &&\n-\t\t    !(argc == 2 && has_dash_dash))\n+\t\t    !(argc == 2 && has_dash_dash) &&\n+\t\t    opts->accept_pathspec)\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n@@ -1262,7 +1270,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1585,6 +1593,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1614,6 +1623,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372674","messageId":"20190329103919.15642-19-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 18/27] switch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:10Z","receivedAt":"2019-03-29T10:41:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and shows worktree\nmodification as a side effect.\n\nMake switch reject this case. Just use \"git status\" if you want\nthat side effect. For switch, you have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0584dc484d..e9dfe38d69 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -55,6 +55,7 @@ struct checkout_opts {\n \tint no_dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_is_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1338,6 +1339,12 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (!opts->switch_branch_doing_nothing_is_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"missing branch or commit argument\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1593,6 +1600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1624,6 +1632,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_is_ok = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372675","messageId":"20190329103919.15642-20-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 19/27] switch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:11Z","receivedAt":"2019-03-29T10:41:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch\" must take a branch (unless you create a new branch,\nthen of course switch can take any commit-ish)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 34 ++++++++++++++++++++++++++++++++++\n 1 file changed, 34 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e9dfe38d69..4416b82bfd 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -45,6 +45,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1298,6 +1299,29 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)\n \treturn status;\n }\n \n+static void die_expecting_a_branch(const struct branch_info *branch_info)\n+{\n+\tstruct object_id oid;\n+\tchar *to_free;\n+\n+\tif (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {\n+\t\tconst char *ref = to_free;\n+\n+\t\tif (skip_prefix(ref, \"refs/tags/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got tag '%s'\"), ref);\n+\t\tif (skip_prefix(ref, \"refs/remotes/\", &ref))\n+\t\t\tdie(_(\"a branch is expected, got remote branch '%s'\"), ref);\n+\t\tdie(_(\"a branch is expected, got '%s'\"), ref);\n+\t}\n+\tif (branch_info->commit)\n+\t\tdie(_(\"a branch is expected, got commit '%s'\"), branch_info->name);\n+\t/*\n+\t * This case should never happen because we already die() on\n+\t * non-commit, but just in case.\n+\t */\n+\tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n+}\n+\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n@@ -1345,6 +1369,14 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"missing branch or commit argument\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->force_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie_expecting_a_branch(new_branch_info);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1602,6 +1634,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1633,6 +1666,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.no_dwim_new_local_branch = 0;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372676","messageId":"20190329103919.15642-21-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 20/27] switch: add short option for --detach","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:12Z","receivedAt":"2019-03-29T10:41:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" automatically detaches branches and --detach is not\nthat useful (--no-detach is more likely). But for \"switch\", you\nmay want to use it more often once you're used to detached HEAD. This\nof course adds -d to git-checkout but it does not harm (yet?) to do it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4416b82bfd..11308ab80c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1423,7 +1423,7 @@ static struct option *add_common_switch_branch_options(\n \tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL('d', \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372677","messageId":"20190329103919.15642-22-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 21/27] switch: implicit dwim, use --no-guess to disable it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:13Z","receivedAt":"2019-03-29T10:42:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is already the default in git-checkout. The real change in here is\njust minor cleanup. The main excuse is to explain why dwim is kept default.\n\nContrary to detach mode that is easy to get into and confusing to get\nback out. Automatically creating a tracking branch often does not kick\nin as often (you would need a branch of the same name on a remote). And\nsince the branch creation is reported clearly, the user should be able\nto undo/delete it if it's unwanted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 38 ++++++++++++++++++++--------------\n builtin/checkout.c             | 16 +++++++-------\n 2 files changed, 31 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex c7192bdefe..58f18a0842 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -31,22 +31,13 @@ branch.\n \t`<branch>`.\n +\n If `<branch>` is not found but there does exist a tracking branch in\n-exactly one remote (call it `<remote>`) with a matching name, treat as\n-equivalent to\n+exactly one remote (call it `<remote>`) with a matching name and\n+`--no-guess` is not specified, treat as equivalent to\n +\n ------------\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n-If the branch exists in multiple remotes and one of them is named by\n-the `checkout.defaultRemote` configuration variable, we'll use that\n-one for the purposes of disambiguation, even if the `<branch>` isn't\n-unique across all remotes. Set it to\n-e.g. `checkout.defaultRemote=origin` to always checkout remote\n-branches from there if `<branch>` is ambiguous but exists on the\n-'origin' remote. See also `checkout.defaultRemote` in\n-linkgit:git-config[1].\n-+\n You could omit `<branch>`, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\n@@ -183,6 +174,27 @@ explicitly give a name with `-b` in such a case.\n \tDo not set up \"upstream\" configuration, even if the\n \t`branch.autoSetupMerge` configuration variable is true.\n \n+--guess::\n+--no-guess::\n+\tIf `<branch>` is not found but there does exist a tracking\n+\tbranch in exactly one remote (call it `<remote>`) with a\n+\tmatching name, treat as equivalent to\n++\n+------------\n+$ git checkout -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n+Use `--no-guess` to disable this.\n+\n -l::\n \tCreate the new branch's reflog; see linkgit:git-branch[1] for\n \tdetails.\n@@ -287,10 +299,6 @@ Note that this option uses the no overlay mode by default (see also\n \tJust like linkgit:git-submodule[1], this will detach `HEAD` of the\n \tsubmodule.\n \n---no-guess::\n-\tDo not attempt to create a branch if a remote tracking branch\n-\tof the same name exists.\n-\n --overlay::\n --no-overlay::\n \tIn the default overlay mode, `git checkout` never\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 11308ab80c..ff9b96993d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -53,7 +53,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint count_checkout_paths;\n \tint overlay_mode;\n-\tint no_dwim_new_local_branch;\n+\tint dwim_new_local_branch;\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n@@ -1430,8 +1430,6 @@ static struct option *add_common_switch_branch_options(\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL(0, \"no-guess\", &opts->no_dwim_new_local_branch,\n-\t\t\t N_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_END()\n@@ -1468,7 +1466,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tint dwim_new_local_branch;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1483,7 +1480,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tdwim_new_local_branch = !opts->no_dwim_new_local_branch;\n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n \t\t\topts->show_progress = 0;\n@@ -1545,7 +1541,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n@@ -1626,12 +1622,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_END()\n \t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n@@ -1656,6 +1654,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"create and switch to a new branch\")),\n \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and switch to a branch\")),\n+\t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n+\t\t\t N_(\"second guess 'git switch <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n \t\t\t N_(\"throw away local modifications\")),\n \t\tOPT_END()\n@@ -1663,7 +1663,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.no_dwim_new_local_branch = 0;\n+\topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.implicit_detach = 0;\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372678","messageId":"20190329103919.15642-23-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 22/27] switch: no worktree status unless real branch switch happens","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:14Z","receivedAt":"2019-03-29T10:42:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When we switch from one branch to another, it makes sense to show a\nsummary of local changes since there could be conflicts, or some files\nleft modified.... When switch is used solely for creating a new\nbranch (and \"switch\" to the same commit) or detaching, we don't really\nneed to show anything.\n\n\"git checkout\" does it anyway for historical reasons. But we can start\nwith a clean slate with switch and don't have to.\n\nThis essentially reverts fa655d8411 (checkout: optimize \"git checkout\n-b <new_branch>\" - 2018-08-16) and make it default for switch,\nbut also for -B and --detach. Users of big repos are encouraged to\nmove to switch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config/checkout.txt |   8 --\n builtin/checkout.c                | 134 ++----------------------------\n t/t1090-sparse-checkout-scope.sh  |  14 ----\n 3 files changed, 8 insertions(+), 148 deletions(-)\n\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex d6872ffa83..6b646813ab 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -16,11 +16,3 @@ will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\n-\n-checkout.optimizeNewBranch::\n-\tOptimizes the performance of \"git checkout -b <new_branch>\" when\n-\tusing sparse-checkout.  When set to true, git will not update the\n-\trepo based on the current sparse-checkout settings.  This means it\n-\twill not update the skip-worktree bit in the index nor add/remove\n-\tfiles in the working directory to reflect the current sparse checkout\n-\tsettings nor will it show the local changes.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ff9b96993d..f7967cdb7c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -26,8 +26,6 @@\n #include \"unpack-trees.h\"\n #include \"xdiff-interface.h\"\n \n-static int checkout_optimize_new_branch;\n-\n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n \tN_(\"git checkout [<options>] [<branch>] -- <file>...\"),\n@@ -57,11 +55,7 @@ struct checkout_opts {\n \tint discard_changes;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n-\n-\t/*\n-\t * If new checkout options are added, skip_merge_working_tree\n-\t * should be updated accordingly.\n-\t */\n+\tint only_merge_on_switching_branches;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -565,112 +559,6 @@ static void setup_branch_path(struct branch_info *branch)\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \n-/*\n- * Skip merging the trees, updating the index and working directory if and\n- * only if we are creating a new branch via \"git checkout -b <new_branch>.\"\n- */\n-static int skip_merge_working_tree(const struct checkout_opts *opts,\n-\tconst struct branch_info *old_branch_info,\n-\tconst struct branch_info *new_branch_info)\n-{\n-\t/*\n-\t * Do the merge if sparse checkout is on and the user has not opted in\n-\t * to the optimized behavior\n-\t */\n-\tif (core_apply_sparse_checkout && !checkout_optimize_new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * We must do the merge if we are actually moving to a new commit.\n-\t */\n-\tif (!old_branch_info->commit || !new_branch_info->commit ||\n-\t\t!oideq(&old_branch_info->commit->object.oid,\n-\t\t       &new_branch_info->commit->object.oid))\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->patch_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * opts->quiet only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * Honor the explicit request for a three-way merge or to throw away\n-\t * local changes\n-\t */\n-\tif (opts->merge || opts->force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * --detach is documented as \"updating the index and the files in the\n-\t * working tree\" but this optimization skips those steps so fall through\n-\t * to the regular code path.\n-\t */\n-\tif (opts->force_detach)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->writeout_stage cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * Honor the explicit ignore requests\n-\t */\n-\tif (!opts->overwrite_ignore || opts->ignore_skipworktree ||\n-\t\topts->ignore_other_worktrees)\n-\t\treturn 0;\n-\n-\t/*\n-\t * opts->show_progress only impacts output so doesn't require a merge\n-\t */\n-\n-\t/*\n-\t * opts->overlay_mode cannot be used with switching branches so is\n-\t * not tested here\n-\t */\n-\n-\t/*\n-\t * If we aren't creating a new branch any changes or updates will\n-\t * happen in the existing branch.  Since that could only be updating\n-\t * the index and working directory, we don't want to skip those steps\n-\t * or we've defeated any purpose in running the command.\n-\t */\n-\tif (!opts->new_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * new_branch_force is defined to \"create/reset and checkout a branch\"\n-\t * so needs to go through the merge to do the reset\n-\t */\n-\tif (opts->new_branch_force)\n-\t\treturn 0;\n-\n-\t/*\n-\t * A new orphaned branch requrires the index and the working tree to be\n-\t * adjusted to <start_point>\n-\t */\n-\tif (opts->new_orphan_branch)\n-\t\treturn 0;\n-\n-\t/*\n-\t * Remaining variables are not checkout options but used to track state\n-\t */\n-\n-\t /*\n-\t  * Do the merge if this is the initial checkout. We cannot use\n-\t  * is_cache_unborn() here because the index hasn't been loaded yet\n-\t  * so cache_nr and timestamp.sec are always zero.\n-\t  */\n-\tif (!file_exists(get_index_file()))\n-\t\treturn 0;\n-\n-\treturn 1;\n-}\n-\n static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t      struct branch_info *old_branch_info,\n \t\t\t      struct branch_info *new_branch_info,\n@@ -1028,6 +916,7 @@ static int switch_branches(const struct checkout_opts *opts,\n \tvoid *path_to_free;\n \tstruct object_id rev;\n \tint flag, writeout_error = 0;\n+\tint do_merge = 1;\n \n \ttrace2_cmd_mode(\"branch\");\n \n@@ -1047,16 +936,12 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\tif (!new_branch_info->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit_or_die(new_branch_info->commit);\n+\n+\t\tif (opts->only_merge_on_switching_branches)\n+\t\t\tdo_merge = 0;\n \t}\n \n-\t/* optimize the \"checkout -b <new_branch> path */\n-\tif (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {\n-\t\tif (!checkout_optimize_new_branch && !opts->quiet) {\n-\t\t\tif (read_cache_preload(NULL) < 0)\n-\t\t\t\treturn error(_(\"index file corrupt\"));\n-\t\t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n-\t\t}\n-\t} else {\n+\tif (do_merge) {\n \t\tret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);\n \t\tif (ret) {\n \t\t\tfree(path_to_free);\n@@ -1076,11 +961,6 @@ static int switch_branches(const struct checkout_opts *opts,\n \n static int git_checkout_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"checkout.optimizenewbranch\")) {\n-\t\tcheckout_optimize_new_branch = git_config_bool(var, value);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(var, \"diff.ignoresubmodules\")) {\n \t\tstruct checkout_opts *opts = cb;\n \t\thandle_ignore_submodules_arg(&opts->diff_options, value);\n@@ -1631,6 +1511,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_is_ok = 1;\n+\topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1666,6 +1547,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_is_ok = 0;\n+\topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex 090b7fc3d3..40cc004326 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '\n \ttest_path_is_file c\n '\n \n-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '\n-\tcp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&\n-\ttest_when_finished \"\n-\t\tmv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout\n-\t\tgit checkout master\n-\t\" &&\n-\techo \"/b\" >>.git/info/sparse-checkout &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit -c checkout.optimizeNewBranch=true checkout -b fast &&\n-\ttest \"$(git ls-files -t b)\" = \"S b\" &&\n-\tgit checkout -b slow &&\n-\ttest \"$(git ls-files -t b)\" = \"H b\"\n-'\n-\n test_expect_success 'merge feature branch into sparse checkout of master' '\n \tgit merge feature &&\n \ttest_path_is_file a &&\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372679","messageId":"20190329103919.15642-24-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 23/27] switch: reject if some operation is in progress","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:15Z","receivedAt":"2019-03-29T10:42:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Unless you know what you're doing, switching to another branch to do\nsomething then switching back could be confusing. Worse, you may even\nforget that you're in the middle of something. By the time you realize,\nyou may have done a ton of work and it gets harder to go back.\n\nA new option --ignore-in-progress was considered but dropped because it\nwas not exactly clear what should happen. Sometimes you can switch away\nand get back safely and resume the operation. Sometimes not. And the\ngit-checkout behavior is automatically clear merge/revert/cherry-pick,\nwhich makes it a bit even more confusing [1].\n\nWe may revisit and add this option in the future. But for now play it\nsafe and not allow it (you can't even skip this check with --force). The\nuser is suggested to cancel the operation by themselves (and hopefully\nthey do consider the consequences, not blindly type the command), or to\ncreate a separate worktree instead of switching. The third option is\nthe good old \"git checkout\", but it's not mentioned.\n\n[1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 40 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f7967cdb7c..5f100c1552 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -24,6 +24,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\n+#include \"wt-status.h\"\n #include \"xdiff-interface.h\"\n \n static const char * const checkout_usage[] = {\n@@ -56,6 +57,7 @@ struct checkout_opts {\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n+\tint can_switch_when_in_progress;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)\n \tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n }\n \n+static void die_if_some_operation_in_progress(void)\n+{\n+\tstruct wt_status_state state;\n+\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(the_repository, &state, 0);\n+\n+\tif (state.merge_in_progress)\n+\t\tdie(_(\"cannot switch branch while merging\\n\"\n+\t\t      \"Consider \\\"git merge --quit\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+\tif (state.am_in_progress)\n+\t\tdie(_(\"cannot switch branch in the middle of an am session\\n\"\n+\t\t      \"Consider \\\"git am --quit\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n+\t\tdie(_(\"cannot switch branch while rebasing\\n\"\n+\t\t      \"Consider \\\"git rebase --quit\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+\tif (state.cherry_pick_in_progress)\n+\t\tdie(_(\"cannot switch branch while cherry-picking\\n\"\n+\t\t      \"Consider \\\"git cherry-pick --quit\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+\tif (state.revert_in_progress)\n+\t\tdie(_(\"cannot switch branch while reverting\\n\"\n+\t\t      \"Consider \\\"git revert --quit\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+\tif (state.bisect_in_progress)\n+\t\tdie(_(\"cannot switch branch while bisecting\\n\"\n+\t\t      \"Consider \\\"git bisect reset HEAD\\\" \"\n+\t\t      \"or \\\"git worktree add\\\".\"));\n+}\n+\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n@@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !new_branch_info->path)\n \t\tdie_expecting_a_branch(new_branch_info);\n \n+\tif (!opts->can_switch_when_in_progress)\n+\t\tdie_if_some_operation_in_progress();\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 0;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n+\topts.can_switch_when_in_progress = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.switch_branch_doing_nothing_is_ok = 0;\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n+\topts.can_switch_when_in_progress = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372680","messageId":"20190329103919.15642-25-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 24/27] switch: make --orphan switch to an empty tree","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:16Z","receivedAt":"2019-03-29T10:42:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Switching and creating branches always involves knowing the\n<start-point> to begin the new branch from. Sometimes, people want to\ncreate a new branch that does not have any commits yet; --orphan is a\nflag to allow that.\n\n--orphan overrides the default of HEAD for <start-point> instead causing\nus to start from an empty history with all tracked files removed from\nthe index and working tree. The use of --orphan is incompatible with\nspecifying a <start-point>.\n\nA note on the implementation. An alternative is just create a dummy\ncommit in-core with empty tree and switch to it. But there's a chance\nthe commit's SHA-1 may end up somewhere permanent like reflog. It's best\nto make sure \"commit\" pointer is NULL to avoid it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 39 +++++++++++++++++++++++++++++++--------\n 1 file changed, 31 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5f100c1552..0351735c6e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -58,6 +58,7 @@ struct checkout_opts {\n \tint switch_branch_doing_nothing_is_ok;\n \tint only_merge_on_switching_branches;\n \tint can_switch_when_in_progress;\n+\tint orphan_from_empty_tree;\n \n \tconst char *new_branch;\n \tconst char *new_branch_force;\n@@ -568,15 +569,21 @@ static int merge_working_tree(const struct checkout_opts *opts,\n {\n \tint ret;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tstruct tree *new_tree;\n \n \thold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);\n \tif (read_cache_preload(NULL) < 0)\n \t\treturn error(_(\"index file corrupt\"));\n \n \tresolve_undo_clear();\n+\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n+\t\tif (new_branch_info->commit)\n+\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n+\t\tnew_tree = parse_tree_indirect(the_hash_algo->empty_tree);\n+\t} else\n+\t\tnew_tree = get_commit_tree(new_branch_info->commit);\n \tif (opts->discard_changes) {\n-\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n-\t\t\t\t opts, 1, writeout_error);\n+\t\tret = reset_tree(new_tree, opts, 1, writeout_error);\n \t\tif (ret)\n \t\t\treturn ret;\n \t} else {\n@@ -614,7 +621,8 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\t\t   &old_branch_info->commit->object.oid :\n \t\t\t\t\t   the_hash_algo->empty_tree);\n \t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n-\t\ttree = parse_tree_indirect(&new_branch_info->commit->object.oid);\n+\t\tparse_tree(new_tree);\n+\t\ttree = new_tree;\n \t\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n \n \t\tret = unpack_trees(2, trees, &topts);\n@@ -663,7 +671,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\n \n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 1,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\n@@ -672,13 +680,13 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.branch1 = new_branch_info->name;\n \t\t\to.branch2 = \"local\";\n \t\t\tret = merge_trees(&o,\n-\t\t\t\t\t  get_commit_tree(new_branch_info->commit),\n+\t\t\t\t\t  new_tree,\n \t\t\t\t\t  work,\n \t\t\t\t\t  get_commit_tree(old_branch_info->commit),\n \t\t\t\t\t  &result);\n \t\t\tif (ret < 0)\n \t\t\t\texit(128);\n-\t\t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n+\t\t\tret = reset_tree(new_tree,\n \t\t\t\t\t opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tstrbuf_release(&o.obuf);\n@@ -696,7 +704,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\tif (!opts->discard_changes && !opts->quiet)\n+\tif (!opts->discard_changes && !opts->quiet && new_branch_info->commit)\n \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n \n \treturn 0;\n@@ -897,7 +905,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \tadd_pending_object(&revs, object, oid_to_hex(&object->oid));\n \n \tfor_each_ref(add_pending_uninteresting_ref, &revs);\n-\tadd_pending_oid(&revs, \"HEAD\", &new_commit->object.oid, UNINTERESTING);\n+\tif (new_commit)\n+\t\tadd_pending_oid(&revs, \"HEAD\",\n+\t\t\t\t&new_commit->object.oid,\n+\t\t\t\tUNINTERESTING);\n \n \tif (prepare_revision_walk(&revs))\n \t\tdie(_(\"internal error in revision walk\"));\n@@ -932,6 +943,14 @@ static int switch_branches(const struct checkout_opts *opts,\n \tif (old_branch_info.path)\n \t\tskip_prefix(old_branch_info.path, \"refs/heads/\", &old_branch_info.name);\n \n+\tif (opts->new_orphan_branch && opts->orphan_from_empty_tree) {\n+\t\tif (new_branch_info->name)\n+\t\t\tBUG(\"'switch --orphan' should never accept a commit as starting point\");\n+\t\tnew_branch_info->commit = NULL;\n+\t\tnew_branch_info->name = \"(empty)\";\n+\t\tdo_merge = 1;\n+\t}\n+\n \tif (!new_branch_info->name) {\n \t\tnew_branch_info->name = \"HEAD\";\n \t\tnew_branch_info->commit = old_branch_info.commit;\n@@ -1268,6 +1287,8 @@ static int checkout_branch(struct checkout_opts *opts,\n \tif (opts->new_orphan_branch) {\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--orphan\", \"-t\");\n+\t\tif (opts->orphan_from_empty_tree && new_branch_info->name)\n+\t\t\tdie(_(\"'%s' cannot take <start-point>\"), \"--orphan\");\n \t} else if (opts->force_detach) {\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n@@ -1553,6 +1574,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \topts.can_switch_when_in_progress = 1;\n+\topts.orphan_from_empty_tree = 0;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1589,6 +1611,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \topts.only_merge_on_switching_branches = 1;\n \topts.implicit_detach = 0;\n \topts.can_switch_when_in_progress = 0;\n+\topts.orphan_from_empty_tree = 1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372681","messageId":"20190329103919.15642-26-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 25/27] t: add tests for switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:17Z","receivedAt":"2019-03-29T10:42:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t2060-switch.sh (new +x) | 96 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 96 insertions(+)\n\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nnew file mode 100755\nindex 0000000000..f9efa29dfb\n--- /dev/null\n+++ b/t/t2060-switch.sh\n@@ -0,0 +1,96 @@\n+#!/bin/sh\n+\n+test_description='switch basic functionality'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit first &&\n+\tgit branch first-branch &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit remote add origin nohost:/nopath &&\n+\tgit update-ref refs/remotes/origin/foo first-branch\n+'\n+\n+test_expect_success 'switch branch no arguments' '\n+\ttest_must_fail git switch\n+'\n+\n+test_expect_success 'switch branch' '\n+\tgit switch first-branch &&\n+\ttest_path_is_missing second.t\n+'\n+\n+test_expect_success 'switch and detach' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch master^{commit} &&\n+\tgit switch --detach master^{commit} &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and detach current branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch master &&\n+\tgit switch --detach &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_expect_success 'switch and create branch' '\n+\ttest_when_finished git switch master &&\n+\tgit switch -c temp master^ &&\n+\ttest_cmp_rev master^ refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'force create branch from HEAD' '\n+\ttest_when_finished git switch master &&\n+\tgit switch --detach master &&\n+\ttest_must_fail git switch -c temp &&\n+\tgit switch -C temp &&\n+\ttest_cmp_rev master refs/heads/temp &&\n+\techo refs/heads/temp >expected-branch &&\n+\tgit symbolic-ref HEAD >actual-branch &&\n+\ttest_cmp expected-branch actual-branch\n+'\n+\n+test_expect_success 'new orphan branch from empty' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch --orphan new-orphan HEAD &&\n+\tgit switch --orphan new-orphan &&\n+\ttest_commit orphan &&\n+\tgit cat-file commit refs/heads/new-orphan >commit &&\n+\t! grep ^parent commit &&\n+\tgit ls-files >tracked-files &&\n+\techo orphan.t >expected &&\n+\ttest_cmp expected tracked-files\n+'\n+\n+test_expect_success 'switching ignores file of same branch name' '\n+\ttest_when_finished git switch master &&\n+\t: >first-branch &&\n+\tgit switch first-branch &&\n+\techo refs/heads/first-branch >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'guess and create branch ' '\n+\ttest_when_finished git switch master &&\n+\ttest_must_fail git switch --no-guess foo &&\n+\tgit switch foo &&\n+\techo refs/heads/foo >expected &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'not switching when something is in progress' '\n+\ttest_when_finished rm -f .git/MERGE_HEAD &&\n+\t# fake a merge-in-progress\n+\tcp .git/HEAD .git/MERGE_HEAD &&\n+\ttest_must_fail git switch -d @^\n+'\n+\n+test_done\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372682","messageId":"20190329103919.15642-27-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 26/27] completion: support switch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:18Z","receivedAt":"2019-03-29T10:42:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Completion support for --guess could be made better. If no --detach is\ngiven, we should only provide a list of refs/heads/* and dwim ones,\nnot the entire ref space. But I still can't penetrate that\n__git_refs() function yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash | 37 +++++++++++++++++++++++++-\n 1 file changed, 36 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 976e4a6548..b24bc48276 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -37,7 +37,8 @@\n #   GIT_COMPLETION_CHECKOUT_NO_GUESS\n #\n #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n-#     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n+#     and git-switch completion (e.g., completing \"foo\" when \"origin/foo\"\n+#     exists).\n \n case \"$COMP_WORDBREAKS\" in\n *:*) : great ;;\n@@ -2158,6 +2159,40 @@ _git_status ()\n \t__git_complete_index_file \"$complete_opt\"\n }\n \n+_git_switch ()\n+{\n+\tcase \"$cur\" in\n+\t--conflict=*)\n+\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t;;\n+\t--*)\n+\t\t__gitcomp_builtin switch\n+\t\t;;\n+\t*)\n+\t\t# check if --track, --no-track, or --no-guess was specified\n+\t\t# if so, disable DWIM mode\n+\t\tlocal track_opt=\"--track\" only_local_ref=n\n+\t\tif [ \"$GIT_COMPLETION_CHECKOUT_NO_GUESS\" = \"1\" ] ||\n+\t\t   [ -n \"$(__git_find_on_cmdline \"--track --no-track --no-guess\")\" ]; then\n+\t\t\ttrack_opt=''\n+\t\tfi\n+\t\t# explicit --guess enables DWIM mode regardless of\n+\t\t# $GIT_COMPLETION_CHECKOUT_NO_GUESS\n+\t\tif [ -n \"$(__git_find_on_cmdline \"--guess\")\" ]; then\n+\t\t\ttrack_opt='--track'\n+\t\tfi\n+\t\tif [ -z \"$(__git_find_on_cmdline \"-d --detach\")\" ]; then\n+\t\t\tonly_local_ref=y\n+\t\tfi\n+\t\tif [ $only_local_ref = y -a -z \"$track_opt\" ]; then\n+\t\t\t__gitcomp_direct \"$(__git_heads \"\" \"$cur\" \" \")\"\n+\t\telse\n+\t\t\t__git_complete_refs $track_opt\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n __git_config_get_set_variables ()\n {\n \tlocal prevword word config_file= c=$cword\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372683","messageId":"20190329103919.15642-28-pclouds@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"[PATCH v6 27/27] doc: promote \"git switch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T10:39:19Z","receivedAt":"2019-03-29T10:42:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new command \"git switch\" is added to avoid the confusion of\none-command-do-all \"git checkout\" for new users. They are also helpful\nto avoid ambiguation context.\n\nFor these reasons, promote it everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n\nThe \"Checking out files\" progress line in unpack-trees.c is also updated\nto \"Updating files\" to be neutral to both git-checkout and git-switch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt           | 12 +++---\n Documentation/git-check-ref-format.txt |  3 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 20 ++++-----\n Documentation/git-stash.txt            |  9 +++--\n Documentation/gitcore-tutorial.txt     | 19 +++++----\n Documentation/giteveryday.txt          | 24 +++++------\n Documentation/gittutorial.txt          |  4 +-\n Documentation/gitworkflows.txt         |  3 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 56 +++++++++++++-------------\n advice.c                               | 12 +++---\n sha1-name.c                            |  2 +-\n t/t2020-checkout-detach.sh             | 24 +++++------\n unpack-trees.c                         |  2 +-\n 19 files changed, 105 insertions(+), 105 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0cd87ddeff..1e2d89b174 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -198,7 +198,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -297,7 +297,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -322,9 +322,9 @@ $ git branch -D test                                    <2>\n NOTES\n -----\n \n-If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n-a branch and check it out with a single command.\n+If you are creating a branch that you want to switch to immediately,\n+it is easier to use the \"git switch\" command with its `-c` option to\n+do the same thing with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n serve four related but different purposes:\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..ee6a4144fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch\" or \"git checkout\" operation.\n+This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..0a24a5679e 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -421,7 +421,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..261d5c1164 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch -c\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 5629ba4c5d..cb6fc166e2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..9659abbf8e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch -c staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe4434ad9d 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 132f8e55f6..cbf901efb4 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the `--amend` option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch topic/wip          <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n-$ work work work       ;# got interrupted\n+$ git switch feature  ;# you were working in \"feature\" branch and\n+$ work work work      ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch -c branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch -c branch2                     <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in `branch1`.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is\n+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is\n     perfect.\n <3> But you can use `reset --keep` to remove the unwanted commit after\n     you switched to `branch2`.\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ebb6282db3 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch -c my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\n@@ -293,7 +293,8 @@ SEE ALSO\n linkgit:git-checkout[1],\n linkgit:git-commit[1],\n linkgit:git-reflog[1],\n-linkgit:git-reset[1]\n+linkgit:git-reset[1],\n+linkgit:git-switch[1]\n \n GIT\n ---\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..f880d21dfb 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git switch -c mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch -c mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch'\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n-$ git reset --hard master^2\n-$ git checkout master\n+$ git switch -C mybranch master^2\n+$ git switch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..ad455f3e39 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -41,7 +41,7 @@ following commands.\n \n   * linkgit:git-log[1] to see what happened.\n \n-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch\n+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch\n     branches.\n \n   * linkgit:git-add[1] to manage the index file.\n@@ -80,7 +80,7 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git switch -c alsa-audio <1>\n $ edit/compile/test\n $ git checkout -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch -c mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch -c private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch -c topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch topic/one && git rebase master <6>\n+$ git switch -C pu next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..e6ad6b5f8d 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt\nindex ca11c7bdaf..abc0dc6bc7 100644\n--- a/Documentation/gitworkflows.txt\n+++ b/Documentation/gitworkflows.txt\n@@ -301,8 +301,7 @@ topics on 'next':\n .Rewind and rebuild next\n [caption=\"Recipe: \"]\n =====================================\n-* `git checkout next`\n-* `git reset --hard master`\n+* `git switch -C next master`\n * `git merge ai/topic_in_next1`\n * `git merge ai/topic_in_next2`\n * ...\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..a1c7a65da6 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch -c mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..94799faa2b 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,\n while heads are expected to advance as development progresses.\n \n Create a new branch head pointing to one of these versions and check it\n-out using linkgit:git-checkout[1]:\n+out using linkgit:git-switch[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch -c new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch -c <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,22 +302,22 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n-accept an arbitrary commit; for example, you can check out the commit\n-referenced by a tag:\n+The `git switch` command normally expects a branch head, but will also\n+accept an arbitrary commit when invoked with --detach; for example,\n+you can check out the commit referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch --detach v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n changes and commit them, and you can discard any commits you make in this\n-state without impacting any branches by performing another checkout.\n+state without impacting any branches by performing another switch.\n \n If you want to create a new branch to retain commits you create, you may\n-do so (now or later) by using -b with the checkout command again. Example:\n+do so (now or later) by using -c with the switch command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch -c new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch -c my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch test && git pull\n+$ git switch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch -c speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch -c mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch -c fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch --detach e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch test\t        # switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch -c new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch -c masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex b224825637..24a7741083 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -191,20 +191,20 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\"\n+\t_(\"Note: switching to '%s'.\\n\"\n \t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"state without impacting any branches by switching back to a branch.\\n\"\n \t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"do so (now or later) by using -c with the switch command. Example:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\"\n+\t\"  git switch -c <new-branch-name>\\n\"\n \t\"\\n\"\n-\t\"Or undo this checkout with:\\n\"\n+\t\"Or undo this operation with:\\n\"\n \t\"\\n\"\n-\t\"  git checkout -\\n\"\n+\t\"  git switch -\\n\"\n \t\"\\n\"\n \t\"Turn off this advice by setting config variable advice.detachedHead to false\\n\\n\");\n \ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..da0518c8e3 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -743,7 +743,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch -c $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex 743c7248a2..b748db9946 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -195,20 +195,20 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n-\tOr undo this checkout with:\n+\tOr undo this operation with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \n@@ -277,20 +277,20 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \t# The first detach operation is more chatty than the following ones.\n \tcat >1st_detach <<-EOF &&\n-\tNote: checking out 'HEAD^'.\n+\tNote: switching to 'HEAD^'.\n \n \tYou are in 'detached HEAD' state. You can look around, make experimental\n \tchanges and commit them, and you can discard any commits you make in this\n-\tstate without impacting any branches by performing another checkout.\n+\tstate without impacting any branches by switching back to a branch.\n \n \tIf you want to create a new branch to retain commits you create, you may\n-\tdo so (now or later) by using -b with the checkout command again. Example:\n+\tdo so (now or later) by using -c with the switch command. Example:\n \n-\t  git checkout -b <new-branch-name>\n+\t  git switch -c <new-branch-name>\n \n-\tOr undo this checkout with:\n+\tOr undo this operation with:\n \n-\t  git checkout -\n+\t  git switch -\n \n \tTurn off this advice by setting config variable advice.detachedHead to false\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 22c41a3ba8..5ff64a983d 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -313,7 +313,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)\n \t\t\ttotal++;\n \t}\n \n-\treturn start_delayed_progress(_(\"Checking out files\"), total);\n+\treturn start_delayed_progress(_(\"Updating files\"), total);\n }\n \n static void setup_collided_checkout_detection(struct checkout *state,\n-- \n2.21.0.479.g47ac719cd3\n\n"},{"id":"372684","messageId":"42ba7f39-4a8e-9866-eebd-2a4dd5ff8414@gmail.com","threadId":"50360","inReplyTo":"CABPp-BHsD=o=3jPKH6gH+XbEdNVqzhr18BSfupbaSNpxAgaqdw@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-29T11:04:19Z","receivedAt":"2019-03-29T11:04:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nOn 28/03/2019 17:39, Elijah Newren wrote:\n> Hi Phillip,\n> \n> On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 28/03/2019 11:04, Duy Nguyen wrote:\n>>> On Wed, Mar 27, 2019 at 5:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>>> On 26/03/2019 15:48, Elijah Newren wrote:\n>>>>> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>>>>>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n> \n>>>>> Yes, and in the middle of a cherry-pick with a range you've added some\n>>>>> commits to one branch and some to another.  In the middle of a revert\n>>>>> you're doing similar.  It sounds like crazytown to me (and maybe we\n>>>>> shouldn't provide the --ignore-in-process flag unless users clamor for\n>>>>> it\n>>>\n>>> I missed this part in my last reading. I think if we could safely\n>>> switch away and get back to resume, then --ignore-in-process could\n>>> still be useful.\n>>\n>> If we can get back safely then that makes sense, I'm not sure about\n>> switching while there are conflicts or staged changes though, it feels\n>> like there's more potential for things to go wrong there.\n> \n> I really like that way of putting it; I think that makes it much\n> clearer.  Note, though that staged changes and conflicts could happen\n> with any of rebase, merge, cherry-pick, or revert, so this problem is\n> not limited to a subset of those operations.\n\nIndeed\n\n> \n>>> I sometimes switch to another commit to check out\n>>> stuff then back. For interactive rebase with \"edit\" command for\n>>> example, it's quite safe to do so. (yes the other option is \"git\n>>> worktree add\", but that could be a heavy hammer sometimes)\n>>\n>>>> I think that could be the way to go for merges and cherry-picks, or\n>>>\n>>> Just so we're clear, what is your \"the way\" to go? to remove\n>>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n>>> --ignore-in-process is specified? Or to leave MERGE_* and\n>>> CHERRY_PICK_HEAD alone and delete other stuff?\n>>\n>> I was agreeing with Elijah about dropping --ignore-in-progress unless\n>> there's a demand for it or at least restricting it so that it requires\n>> --discard-changes and aborts in-progress merges and single in-progress\n>> cherry-picks/reverts. (I'm worried about people switching branches when\n>> cherry-picking more than one commit, though as you say it can make sense\n>> during a rebase.)\n> \n> I understand the desire to prevent mis-uses, and I agree that if there\n> are staged changes or conflicts it's really likely things will go\n> sideways.  But I think we should instead check for those situations\n> rather than use e.g. rebase vs. merge as a proxy for whether those\n> problems could be present.\n\nWhen cherry-picking multiple commits if the user commits the conflict \nresolution with 'git commit' then the presence of .git/sequencer is the \nonly sign that a cherry-pick is in progress (wt-status.c fails to detect \nthis, I've got a fix but no tests yet). rebase can also stop without \nhaving conflicts or staged changes so I think we need to check for in \nprogress commands as well as conflicts (what do we want to do if someone \ntries to switch in the middle of a bisect? - I don't have a strong \nopinion). I agree switch should fail if there are conflicts, but I think \nit is fine to switch with staged or unstaged changes if there isn't a \nmerge etc in progress (I quite often start working on something and \nthen realize I haven't started a new branch just before I commit). I \ncould possibly be convinced that silently switching with staged changes \nis always a bad idea though.\n\n> I am especially concerned with the idea of\n> having something like \"git switch --ignore-in-progress\n> --discard-changes\" being used to quit merges or cherry-picks or\n> reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git\n> switch) in a way that is far less clear.  I think that's a bad route\n> to go down, and we should keep the commands orthogonal \n\nkeeping commands orthogonal is certainly clearer, if less convenient - \nlets do it (assuming Duy agrees).\n\n> (if I could\n> start all over, I'd also make reset and checkout and everything else\n> stop modifying any in-progress state).\n> \n> Instead, I would either:\n> \n>    * Drop `--ignore-in-progress` for now.  (Although Duy had a\n> meaningful usecase)\n\nI think it could be useful during a rebase, I'm not sure about any of \nthe other operations though.\n\n> \n> OR\n> \n>    * Make `git switch --ignore-in-progress <branch>` leave all process\n> state in place and switch branches, if we would otherwise be able to\n> switch branches (i.e. there isn't dirty or conflicted changes in the\n> way).\n\nI thought we allowed branch switches when there are staged or unstaged \nchanges, I don't think that is a problem unless we're in the middle of a \nmerge etc. I'm still not sure it's a good idea to switch branches in the \nmiddle of a multiple cherry-pick, maybe we should print a warning.\n\n>  BUT, make sure to also:\n>    * Make '--ignore-in-progress' incompatible with both '-m' and\n> '--discard-changes'; if folks try to use either of those additional\n> options with --ignore-in-progress, tell people to use `<operation>\n> --quit` first.\n\nI think of --discard-changes like --abort. --quit only removes the state \ndir so would pair with -m, as it does not reset the index or working tree.\n\nOverall I think we're more or less in agreement modulo the treatment of \nstaged changes when there is no merge etc in progress.\n\nBest Wishes\n\nPhillip\n\n>  Do NOT provide an override. (Alternatively, refer to\n> `<operation> --quit` as the override, since it is).\n> \n> \n> Elijah\n> \n"},{"id":"372686","messageId":"CACsJy8AVGoNvU9yC=y5BvNWQaMachD-nU-OVXuFVAw62_+mxKg@mail.gmail.com","threadId":"50360","inReplyTo":"42ba7f39-4a8e-9866-eebd-2a4dd5ff8414@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-29T11:27:04Z","receivedAt":"2019-03-29T11:27:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 29, 2019 at 6:04 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > I am especially concerned with the idea of\n> > having something like \"git switch --ignore-in-progress\n> > --discard-changes\" being used to quit merges or cherry-picks or\n> > reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git\n> > switch) in a way that is far less clear.  I think that's a bad route\n> > to go down, and we should keep the commands orthogonal\n>\n> keeping commands orthogonal is certainly clearer, if less convenient -\n> lets do it (assuming Duy agrees).\n\nI sent out v6 a bit too early I guess. But removing\n--ignore-in-progress also makes it orthogonal. You could cancel the\nin-progress operation and resume switching, or find another way and\nforget about switching.\n-- \nDuy\n"},{"id":"372706","messageId":"CABPp-BGE49LyxQSMd8v_af=X_N9b0GyOk6TMK6jb8w1GMy6x+g@mail.gmail.com","threadId":"50360","inReplyTo":"42ba7f39-4a8e-9866-eebd-2a4dd5ff8414@gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-29T15:35:34Z","receivedAt":"2019-03-29T15:35:50Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Phillip,\n\nOn Fri, Mar 29, 2019 at 4:04 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 28/03/2019 17:39, Elijah Newren wrote:\n> > On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >> On 28/03/2019 11:04, Duy Nguyen wrote:\n\n> >>> Just so we're clear, what is your \"the way\" to go? to remove\n> >>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n> >>> --ignore-in-process is specified? Or to leave MERGE_* and\n> >>> CHERRY_PICK_HEAD alone and delete other stuff?\n> >>\n> >> I was agreeing with Elijah about dropping --ignore-in-progress unless\n> >> there's a demand for it or at least restricting it so that it requires\n> >> --discard-changes and aborts in-progress merges and single in-progress\n> >> cherry-picks/reverts. (I'm worried about people switching branches when\n> >> cherry-picking more than one commit, though as you say it can make sense\n> >> during a rebase.)\n> >\n> > I understand the desire to prevent mis-uses, and I agree that if there\n> > are staged changes or conflicts it's really likely things will go\n> > sideways.  But I think we should instead check for those situations\n> > rather than use e.g. rebase vs. merge as a proxy for whether those\n> > problems could be present.\n>\n> When cherry-picking multiple commits if the user commits the conflict\n> resolution with 'git commit' then the presence of .git/sequencer is the\n> only sign that a cherry-pick is in progress (wt-status.c fails to detect\n> this, I've got a fix but no tests yet). rebase can also stop without\n> having conflicts or staged changes so I think we need to check for in\n> progress commands as well as conflicts (what do we want to do if someone\n\nThis whole discussion is about \"--ignore-in-progress\" which implicitly\nimplies we are checking for in progress commands and choosing whether\nto override it.  So I don't understand what you mean by saying we need\nto check for it; isn't that a given?\n\n> tries to switch in the middle of a bisect? - I don't have a strong\n> opinion). I agree switch should fail if there are conflicts, but I think\n> it is fine to switch with staged or unstaged changes if there isn't a\n> merge etc in progress (I quite often start working on something and\n> then realize I haven't started a new branch just before I commit). I\n> could possibly be convinced that silently switching with staged changes\n> is always a bad idea though.\n\nI think we might be missing the big picture by trying to discuss\nthings in terms of in-progress operations or conflicts or staged\nchanges or unstaged changes.  Allow me to attempt to reframe the\ndiscussion:  We have identified at least one case where allowing the\n--ignore-in-progress flag would be unsafe, and we've identified one\nwhere we think it would be safe and useful, thus we need a rule of\nthumb for when it is safe to use and when it isn't.  Here's my\nattempt:\n\n  --ignore-in-progress is safe enough for usage if we can switch to\nanother branch and back with no net overall changes to either the\nindex or the working tree after the two switches.\n\nThis rule could allow for the presence of both staged and unstaged\nchanges (or maybe even conflicts in some alternate world where\ncheckout/switch didn't necessarily error out on those), depending on\nif switch/checkout can operate without touching those particular files\nas part of switching.\n\n> > I am especially concerned with the idea of\n> > having something like \"git switch --ignore-in-progress\n> > --discard-changes\" being used to quit merges or cherry-picks or\n> > reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git\n> > switch) in a way that is far less clear.  I think that's a bad route\n> > to go down, and we should keep the commands orthogonal\n>\n> keeping commands orthogonal is certainly clearer, if less convenient -\n> lets do it (assuming Duy agrees).\n\nYaay!\n\n> > (if I could\n> > start all over, I'd also make reset and checkout and everything else\n> > stop modifying any in-progress state).\n> >\n> > Instead, I would either:\n> >\n> >    * Drop `--ignore-in-progress` for now.  (Although Duy had a\n> > meaningful usecase)\n>\n> I think it could be useful during a rebase, I'm not sure about any of\n> the other operations though.\n\nI think it could be useful during some rebases, but it should not be\nallowed if the user can't switch back to the current commit with no\nnet overall changes to the index or working tree.\n\nAlso, I don't see how rebase is unique here.  Rebase, cherry-pick,\nmerge, and revert can all stop with conflicts, staged changes, and\nunstaged changes.  All of them can also stop without any one of those\n(e.g. cherry-pick'ing a commit which has been piecemeal applied\nalready, merging a branch whose individual changes have already been\ncherry-picked and when the user has specified --no-commit, or\nreverting a commit whose changes have already been unapplied).  Thus,\nI continue to believe that which operation is in progress is\nirrelevant.  Either we shouldn't allow switching during any\nin-progress operation, or we should determine some other criteria for\nwhen it is safe to allow --ignore-in-progress.  Basing it on the\noperation would sometimes allow --ignore-in-progress to be used when\nit shouldn't be, and disallow it sometimes when it shouldn't.  I'm a\nfan of the rule I mentioned up above (\"if we can switch and switch\nback with no net changes then it's safe enough to allow\")\n\n> >\n> > OR\n> >\n> >    * Make `git switch --ignore-in-progress <branch>` leave all process\n> > state in place and switch branches, if we would otherwise be able to\n> > switch branches (i.e. there isn't dirty or conflicted changes in the\n> > way).\n>\n> I thought we allowed branch switches when there are staged or unstaged\n> changes, I don't think that is a problem unless we're in the middle of a\n> merge etc. I'm still not sure it's a good idea to switch branches in the\n> middle of a multiple cherry-pick, maybe we should print a warning.\n\nI didn't say to disallow it if there were dirty or conflicted changes,\nI said to disallow it if there were dirty or conflicted changes *in\nthe way*.  We don't allow branch switches when dirty changes would be\noverwritten or need to be merged, as that can't easily be reversed.  I\nthink --ignore-in-progress should only be allowed when it can be\neasily reversed to get the user back to the right branch/commit.\n\nThis \"no net changes\" rule also reinforces (or is reinforced by) the\nother suggestion I made of having --ignore-in-progress be made\nincompatible with both -m and --discard-changes.\n\nBut I totally agree that switching branches during the middle of some\noperation should print a warning -- not just for cherry-pick, but for\nmerge or rebase or revert too.  In all cases it'll be important to\ntell the user both that they could really mess things up if they try\nto resume the operation without switching back, and telling the user\nhow to get back to where they used to be (in rebase's case, that'd be\n\"git switch --ignore-in-progress <previous-commit>\" while for the\nother three it'd be \"git switch --ignore-in-progress\n<previous-branch>\").\n"},{"id":"372708","messageId":"CABPp-BHX1gRhTdurAwrPg60Hk-OuhbrEN=4zatx4OOUo-DkQvw@mail.gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-24-pclouds@gmail.com","subject":"Re: [PATCH v6 23/27] switch: reject if some operation is in progress","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-29T15:47:06Z","receivedAt":"2019-03-29T15:47:20Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 29, 2019 at 3:42 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Unless you know what you're doing, switching to another branch to do\n> something then switching back could be confusing. Worse, you may even\n> forget that you're in the middle of something. By the time you realize,\n> you may have done a ton of work and it gets harder to go back.\n>\n> A new option --ignore-in-progress was considered but dropped because it\n> was not exactly clear what should happen. Sometimes you can switch away\n> and get back safely and resume the operation. Sometimes not. And the\n> git-checkout behavior is automatically clear merge/revert/cherry-pick,\n> which makes it a bit even more confusing [1].\n>\n> We may revisit and add this option in the future. But for now play it\n> safe and not allow it (you can't even skip this check with --force). The\n> user is suggested to cancel the operation by themselves (and hopefully\n> they do consider the consequences, not blindly type the command), or to\n> create a separate worktree instead of switching. The third option is\n> the good old \"git checkout\", but it's not mentioned.\n\nI think these safety checks are pretty important for checkout too...\n\n>\n> [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 40 insertions(+)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index f7967cdb7c..5f100c1552 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -24,6 +24,7 @@\n>  #include \"tree.h\"\n>  #include \"tree-walk.h\"\n>  #include \"unpack-trees.h\"\n> +#include \"wt-status.h\"\n>  #include \"xdiff-interface.h\"\n>\n>  static const char * const checkout_usage[] = {\n> @@ -56,6 +57,7 @@ struct checkout_opts {\n>         int accept_pathspec;\n>         int switch_branch_doing_nothing_is_ok;\n>         int only_merge_on_switching_branches;\n> +       int can_switch_when_in_progress;\n>\n>         const char *new_branch;\n>         const char *new_branch_force;\n> @@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)\n>         die(_(\"a branch is expected, got '%s'\"), branch_info->name);\n>  }\n>\n> +static void die_if_some_operation_in_progress(void)\n> +{\n> +       struct wt_status_state state;\n> +\n> +       memset(&state, 0, sizeof(state));\n> +       wt_status_get_state(the_repository, &state, 0);\n> +\n> +       if (state.merge_in_progress)\n> +               die(_(\"cannot switch branch while merging\\n\"\n> +                     \"Consider \\\"git merge --quit\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +       if (state.am_in_progress)\n> +               die(_(\"cannot switch branch in the middle of an am session\\n\"\n> +                     \"Consider \\\"git am --quit\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +       if (state.rebase_interactive_in_progress || state.rebase_in_progress)\n> +               die(_(\"cannot switch branch while rebasing\\n\"\n> +                     \"Consider \\\"git rebase --quit\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +       if (state.cherry_pick_in_progress)\n> +               die(_(\"cannot switch branch while cherry-picking\\n\"\n> +                     \"Consider \\\"git cherry-pick --quit\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +       if (state.revert_in_progress)\n> +               die(_(\"cannot switch branch while reverting\\n\"\n> +                     \"Consider \\\"git revert --quit\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +       if (state.bisect_in_progress)\n> +               die(_(\"cannot switch branch while bisecting\\n\"\n> +                     \"Consider \\\"git bisect reset HEAD\\\" \"\n> +                     \"or \\\"git worktree add\\\".\"));\n> +}\n> +\n>  static int checkout_branch(struct checkout_opts *opts,\n>                            struct branch_info *new_branch_info)\n>  {\n> @@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,\n>             !new_branch_info->path)\n>                 die_expecting_a_branch(new_branch_info);\n>\n> +       if (!opts->can_switch_when_in_progress)\n> +               die_if_some_operation_in_progress();\n> +\n>         if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n>             !opts->ignore_other_worktrees) {\n>                 int flag;\n> @@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>         opts.only_merge_on_switching_branches = 0;\n>         opts.accept_pathspec = 1;\n>         opts.implicit_detach = 1;\n> +       opts.can_switch_when_in_progress = 1;\n\nI think this should be 0 too; this check is good for both checkout and\nswitch.  And if people really do want to use it during in-progress\noperations because it is sometimes safe enough, then both operations\ndeserve some kind of override flag that checks for the appropriate\nsafety conditions (as we're discussing in the other thread) and then\nallows or rejects it.\n\nHowever, I'm totally fine with proposing another patch after your\nseries lands to do all of this; this patch is fine as-is for now.\n\n>         options = parse_options_dup(checkout_options);\n>         options = add_common_options(&opts, options);\n> @@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>         opts.switch_branch_doing_nothing_is_ok = 0;\n>         opts.only_merge_on_switching_branches = 1;\n>         opts.implicit_detach = 0;\n> +       opts.can_switch_when_in_progress = 0;\n>\n>         options = parse_options_dup(switch_options);\n>         options = add_common_options(&opts, options);\n> --\n> 2.21.0.479.g47ac719cd3\n"},{"id":"372712","messageId":"CABPp-BGFNzroixaO1QN7Q_tepTzeFGjd5-=M3Rfv2CH+pHjaLw@mail.gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-1-pclouds@gmail.com","subject":"Re: [PATCH v6 00/27] Add new command 'switch'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-29T15:58:49Z","receivedAt":"2019-03-29T15:59:03Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Mar 29, 2019 at 3:39 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> This adds a new command 'git-switch' as the half-replacement for\n> 'git-checkout'. Jump to 13/27 as the starting point. The other half is\n> git-restore, which is dealt with separately.\n>\n> The sixth time must be the charm, because the seventh time may be the\n> curse!\n>\n> The last big change since v5 is from the discussion about\n> remove_branch_state() and its relation with --ignore-in-progress. By\n> popular demand, --ignore-in-progress is gone. The discussion is linked\n> in 23/27, which is also improved to suggest a way out.\n>\n> That discussion also leads to a new patch, 07/27, which improves 'git\n> checkout' and tells the user when merge/revert/cherry-pick is canceled.\n>\n> The rest is just minor updates here and there. And I made sure the\n> sign-off is back this time :D\n\nI looked through the range-diff and it looks good to me.  I think the\nsafety we added to switch could also be added to checkout, and I think\nwe're narrowing down on how to safely support --ignore-in-progress in\ncertain situations, but both of those things could go in after this\nseries lands...and both might generate more discussion that doesn't\nneed to derail the rest of this series.\n"},{"id":"372863","messageId":"xmqqbm1qxee5.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"CABPp-BFTyALWmnJ=dT1xNivjcQhtKak15ydfkYjEsEC-j4BD9w@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-01T09:29:06Z","receivedAt":"2019-04-01T09:29:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Tue, Mar 26, 2019 at 8:24 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>> On Tue, Mar 26, 2019 at 10:01 PM Elijah Newren <newren@gmail.com> wrote:\n>\n>> Yeah.. --ignore-in-process does not necessarily mean aborting\n>> something when you just want to get out to examine some other commit.\n>> And I agree doing nothing seems like the best (or least\n>> confusing/surprising) option.\n> ...\n> Yes, and in the middle of a cherry-pick with a range you've added some\n> commits to one branch and some to another.  In the middle of a revert\n> you're doing similar.  It sounds like crazytown to me (and maybe we\n> shouldn't provide the --ignore-in-process flag unless users clamor for\n> it ...\n\nIt really sounds like --ignore-in-process that _leaves_ the state\nfiles around which are known to be unrelated to the new commit\npointed out by HEAD is asking for trouble.\n\nWe just should tell the users \"if you want to go examine something\nelse during an in-progress operation, do it elsewhere, or cancel the\nin-progress operation with 'cmd --abort' (or perhaps 'reset --hard')\nwhich will allow 'switch' to move away\".\n\nThe thing is, \"switch temporarily to another commit, do some\nsightseeing and then switch back to the original commit--we did not\ntouch the in-progress state files while doing so, so we ought to\ncome back to exactly the same state to be able to continue\" is\nbrittle.  Temporarily switching to another commit or branch would\nneed to touch the index (in some irrevocable way) and with future\nversions of Git, underlying machineries (like UNDO data) other than\nthe index as well.  What the user would do after (temporarily)\nswitching out of the in-process state (e.g. controlled by the\nsequencer) may be destructive (e.g. \"switch away temporarily, forget\nthat you've switched away and try to run cherry-pick or rebase\").\n\n"},{"id":"374485","messageId":"17402ee1-68c1-e51f-dc32-0795e0b63bc8@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-16-pclouds@gmail.com","subject":"Re: [PATCH v6 15/27] switch: add --discard-changes","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-25T10:02:41Z","receivedAt":"2019-04-25T10:02:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n> --discard-changes is a better name than --force for this option since\n> it's what really happens.\n\nI didn't realize when I suggested the name that --force overwrites\nuntracked files as well as discarding changes from tracked files. I\nthink we should document that. It would be nice if read-tree --reset -u\ntook an optional argument so read-tree --reset=tracked -u would not\noverwrite untracked files. Then we could have --discard-changes just\ndiscard the changes and not overwrite untracked files. I had a quick\nlook at unpack trees and it looks like a fairly straight forward change\n(famous last words) - perhaps I'll have a go at it next week.\n\nBest Wishes\n\nPhillip\n\n> --force is turned to an alias for\n> --discard-changes. But it's meant to be an alias for potentially more\n> force options in the future.\n> \n> Side note. It's not obvious from the patch but --discard-changes also\n> affects submodules if --recurse-submodules is used. The knob to force\n> updating submodules is hidden behind unpack-trees.c\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 12 ++++++++++--\n>  1 file changed, 10 insertions(+), 2 deletions(-)\n> \n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 319ba372e3..6d0b2ef565 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -53,6 +53,7 @@ struct checkout_opts {\n>  \tint count_checkout_paths;\n>  \tint overlay_mode;\n>  \tint no_dwim_new_local_branch;\n> +\tint discard_changes;\n>  \n>  \t/*\n>  \t * If new checkout options are added, skip_merge_working_tree\n> @@ -680,7 +681,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>  \t\treturn error(_(\"index file corrupt\"));\n>  \n>  \tresolve_undo_clear();\n> -\tif (opts->force) {\n> +\tif (opts->discard_changes) {\n>  \t\tret = reset_tree(get_commit_tree(new_branch_info->commit),\n>  \t\t\t\t opts, 1, writeout_error);\n>  \t\tif (ret)\n> @@ -802,7 +803,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>  \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n>  \t\tdie(_(\"unable to write new index file\"));\n>  \n> -\tif (!opts->force && !opts->quiet)\n> +\tif (!opts->discard_changes && !opts->quiet)\n>  \t\tshow_local_changes(&new_branch_info->commit->object, &opts->diff_options);\n>  \n>  \treturn 0;\n> @@ -1309,6 +1310,9 @@ static int checkout_branch(struct checkout_opts *opts,\n>  \tif (opts->force && opts->merge)\n>  \t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-f\", \"-m\");\n>  \n> +\tif (opts->discard_changes && opts->merge)\n> +\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--discard-changes\", \"--merge\");\n> +\n>  \tif (opts->force_detach && opts->new_branch)\n>  \t\tdie(_(\"'%s' cannot be used with '%s'\"),\n>  \t\t    \"--detach\", \"-b/-B/--orphan\");\n> @@ -1445,6 +1449,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n>  \t\topts->merge = 1; /* implied */\n>  \t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n>  \t}\n> +\tif (opts->force)\n> +\t\topts->discard_changes = 1;\n>  \n>  \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n>  \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n> @@ -1600,6 +1606,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>  \t\t\t   N_(\"create and switch to a new branch\")),\n>  \t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n>  \t\t\t   N_(\"create/reset and switch to a branch\")),\n> +\t\tOPT_BOOL(0, \"discard-changes\", &opts.discard_changes,\n> +\t\t\t N_(\"throw away local modifications\")),\n>  \t\tOPT_END()\n>  \t};\n>  \tint ret;\n> \n\n"},{"id":"374488","messageId":"CACsJy8BF5DwykEmmPGDR7+AzhOAFreJnML073K0xNBdTDjqMJg@mail.gmail.com","threadId":"50360","inReplyTo":"17402ee1-68c1-e51f-dc32-0795e0b63bc8@gmail.com","subject":"Re: [PATCH v6 15/27] switch: add --discard-changes","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-25T10:12:15Z","receivedAt":"2019-04-25T10:13:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 25, 2019 at 5:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n> > --discard-changes is a better name than --force for this option since\n> > it's what really happens.\n>\n> I didn't realize when I suggested the name that --force overwrites\n> untracked files as well as discarding changes from tracked files. I\n> think we should document that. It would be nice if read-tree --reset -u\n> took an optional argument so read-tree --reset=tracked -u would not\n> overwrite untracked files. Then we could have --discard-changes just\n> discard the changes and not overwrite untracked files. I had a quick\n> look at unpack trees and it looks like a fairly straight forward change\n> (famous last words) - perhaps I'll have a go at it next week.\n\nSo, --discard-changes is all about tracked changes, and we may have\n--overwrite-untracked to cover the other part, and --force enables\nboth? That does not sound so bad (and maybe a good cure for those\n\"overwriting untracked\" reports we've seen quite often lately).\n\nGood luck with unpack-trees.c. But if it turns out you're too busy,\njust let me know if want to hand that back to me.\n-- \nDuy\n"},{"id":"374490","messageId":"81a1e5d3-5396-f556-ac77-4115f352f391@gmail.com","threadId":"50360","inReplyTo":"CABPp-BGE49LyxQSMd8v_af=X_N9b0GyOk6TMK6jb8w1GMy6x+g@mail.gmail.com","subject":"Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-25T10:20:30Z","receivedAt":"2019-04-25T10:20:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nSorry it's taken me so long to reply to this - I'm not sure where the\nlast few weeks have gone.\n\nOn 29/03/2019 15:35, Elijah Newren wrote:\n> Hi Phillip,\n> \n> On Fri, Mar 29, 2019 at 4:04 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 28/03/2019 17:39, Elijah Newren wrote:\n>>> On Thu, Mar 28, 2019 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>>> On 28/03/2019 11:04, Duy Nguyen wrote:\n> \n>>>>> Just so we're clear, what is your \"the way\" to go? to remove\n>>>>> CHERRY_HEAD_PICK and MERGE_HEAD (and other MERGE_* as well) if\n>>>>> --ignore-in-process is specified? Or to leave MERGE_* and\n>>>>> CHERRY_PICK_HEAD alone and delete other stuff?\n>>>>\n>>>> I was agreeing with Elijah about dropping --ignore-in-progress unless\n>>>> there's a demand for it or at least restricting it so that it requires\n>>>> --discard-changes and aborts in-progress merges and single in-progress\n>>>> cherry-picks/reverts. (I'm worried about people switching branches when\n>>>> cherry-picking more than one commit, though as you say it can make sense\n>>>> during a rebase.)\n>>>\n>>> I understand the desire to prevent mis-uses, and I agree that if there\n>>> are staged changes or conflicts it's really likely things will go\n>>> sideways.  But I think we should instead check for those situations\n>>> rather than use e.g. rebase vs. merge as a proxy for whether those\n>>> problems could be present.\n>>\n>> When cherry-picking multiple commits if the user commits the conflict\n>> resolution with 'git commit' then the presence of .git/sequencer is the\n>> only sign that a cherry-pick is in progress (wt-status.c fails to detect\n>> this, I've got a fix but no tests yet). rebase can also stop without\n>> having conflicts or staged changes so I think we need to check for in\n>> progress commands as well as conflicts (what do we want to do if someone\n> \n> This whole discussion is about \"--ignore-in-progress\" which implicitly\n> implies we are checking for in progress commands and choosing whether\n> to override it.  So I don't understand what you mean by saying we need\n> to check for it; isn't that a given?\n\nI think I was confused \"instead\" above. Anyway we both agree that those\nchecks are in addition to the in-progress ones.\n\n>> tries to switch in the middle of a bisect? - I don't have a strong\n>> opinion). I agree switch should fail if there are conflicts, but I think\n>> it is fine to switch with staged or unstaged changes if there isn't a\n>> merge etc in progress (I quite often start working on something and\n>> then realize I haven't started a new branch just before I commit). I\n>> could possibly be convinced that silently switching with staged changes\n>> is always a bad idea though.\n> \n> I think we might be missing the big picture by trying to discuss\n> things in terms of in-progress operations or conflicts or staged\n> changes or unstaged changes.  Allow me to attempt to reframe the\n> discussion:  We have identified at least one case where allowing the\n> --ignore-in-progress flag would be unsafe, and we've identified one\n> where we think it would be safe and useful, thus we need a rule of\n> thumb for when it is safe to use and when it isn't.  Here's my\n> attempt:\n> \n>   --ignore-in-progress is safe enough for usage if we can switch to\n> another branch and back with no net overall changes to either the\n> index or the working tree after the two switches.\n\nMy worry is that someone will switch branches and commit by accident\nbefore switching back again. Having said that we require them to specify\n--ignore-in-progress so we have a safe default. I think your rule is\nprobably a good balance between safety and convenience, though it is\npotentially confusing as it will allow --ignore-in-progress to switch\nsometimes but not others.\n> This rule could allow for the presence of both staged and unstaged\n> changes (or maybe even conflicts in some alternate world where\n> checkout/switch didn't necessarily error out on those), depending on\n> if switch/checkout can operate without touching those particular files\n> as part of switching.\n> \n>>> I am especially concerned with the idea of\n>>> having something like \"git switch --ignore-in-progress\n>>> --discard-changes\" being used to quit merges or cherry-picks or\n>>> reverts or even rebases. In my opinion, doing so is creating flags to > combine uncommon pairs of git commands (git <operation> --quit + git\n>>> switch) in a way that is far less clear.  I think that's a bad route\n>>> to go down, and we should keep the commands orthogonal\n>>\n>> keeping commands orthogonal is certainly clearer, if less convenient -\n>> lets do it (assuming Duy agrees).\n> \n> Yaay!\n> \n>>> (if I could\n>>> start all over, I'd also make reset and checkout and everything else\n>>> stop modifying any in-progress state).\n>>>\n>>> Instead, I would either:\n>>>\n>>>    * Drop `--ignore-in-progress` for now.  (Although Duy had a\n>>> meaningful usecase)\n>>\n>> I think it could be useful during a rebase, I'm not sure about any of\n>> the other operations though.\n> \n> I think it could be useful during some rebases, but it should not be\n> allowed if the user can't switch back to the current commit with no\n> net overall changes to the index or working tree.\n> \n> Also, I don't see how rebase is unique here.  Rebase, cherry-pick,\n> merge, and revert can all stop with conflicts, staged changes, and\n> unstaged changes.  All of them can also stop without any one of those\n> (e.g. cherry-pick'ing a commit which has been piecemeal applied\n> already, merging a branch whose individual changes have already been\n> cherry-picked and when the user has specified --no-commit, or\n> reverting a commit whose changes have already been unapplied).  Thus,\n> I continue to believe that which operation is in progress is\n> irrelevant.  Either we shouldn't allow switching during any\n> in-progress operation, or we should determine some other criteria for\n> when it is safe to allow --ignore-in-progress.  Basing it on the\n> operation would sometimes allow --ignore-in-progress to be used when\n> it shouldn't be, and disallow it sometimes when it shouldn't.  I'm a\n> fan of the rule I mentioned up above (\"if we can switch and switch\n> back with no net changes then it's safe enough to allow\")\n\nYou're right that there is nothing intrinsically special about rebases,\nit's just that in practice I think it is more useful in that case (I\ndon't remember wanting to switch branches while cherry-picking etc but\nmaybe that because I use them less). Having a consistent rule as you\nsuggest would be less confusing.\n\n>>> OR\n>>>\n>>>    * Make `git switch --ignore-in-progress <branch>` leave all process\n>>> state in place and switch branches, if we would otherwise be able to\n>>> switch branches (i.e. there isn't dirty or conflicted changes in the\n>>> way).\n>>\n>> I thought we allowed branch switches when there are staged or unstaged\n>> changes, I don't think that is a problem unless we're in the middle of a\n>> merge etc. I'm still not sure it's a good idea to switch branches in the\n>> middle of a multiple cherry-pick, maybe we should print a warning.\n> \n> I didn't say to disallow it if there were dirty or conflicted changes,\n> I said to disallow it if there were dirty or conflicted changes *in\n> the way*.  We don't allow branch switches when dirty changes would be\n> overwritten or need to be merged, as that can't easily be reversed.  I\n> think --ignore-in-progress should only be allowed when it can be\n> easily reversed to get the user back to the right branch/commit.\n> \n> This \"no net changes\" rule also reinforces (or is reinforced by) the\n> other suggestion I made of having --ignore-in-progress be made\n> incompatible with both -m and --discard-changes.\n> \n> But I totally agree that switching branches during the middle of some\n> operation should print a warning -- not just for cherry-pick, but for\n> merge or rebase or revert too.  In all cases it'll be important to\n> tell the user both that they could really mess things up if they try\n> to resume the operation without switching back, and telling the user\n> how to get back to where they used to be (in rebase's case, that'd be\n> \"git switch --ignore-in-progress <previous-commit>\" while for the\n> other three it'd be \"git switch --ignore-in-progress\n> <previous-branch>\").\n\nGiving the user instructions for how to switch back is a really good idea.\n\nBest Wishes\n\nPhillip\n> \n\n"},{"id":"374491","messageId":"78c7c281-82ec-2ba9-a607-dd2ecba54945@gmail.com","threadId":"50360","inReplyTo":"20190329103919.15642-24-pclouds@gmail.com","subject":"Re: [PATCH v6 23/27] switch: reject if some operation is in progress","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-25T10:33:14Z","receivedAt":"2019-04-25T10:33:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n> Unless you know what you're doing, switching to another branch to do\n> something then switching back could be confusing. Worse, you may even\n> forget that you're in the middle of something. By the time you realize,\n> you may have done a ton of work and it gets harder to go back.\n> \n> A new option --ignore-in-progress was considered but dropped because it\n> was not exactly clear what should happen. Sometimes you can switch away\n> and get back safely and resume the operation. Sometimes not. And the\n> git-checkout behavior is automatically clear merge/revert/cherry-pick,\n> which makes it a bit even more confusing [1].\n> \n> We may revisit and add this option in the future. But for now play it\n> safe and not allow it (you can't even skip this check with --force).\n\nI think this is a good compromise, lets see how it goes (I think I\nbroadly agree with Elijah's suggestion to allow the switch if we can\nsafely switch back again if we want to add --ignore-in-progress in the\nfuture).\n\n> The\n> user is suggested to cancel the operation by themselves (and hopefully\n> they do consider the consequences, not blindly type the command), or to\n> create a separate worktree instead of switching. The third option is\n> the good old \"git checkout\", but it's not mentioned.\n> \n> [1] CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 40 ++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 40 insertions(+)\n> \n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index f7967cdb7c..5f100c1552 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -24,6 +24,7 @@\n>  #include \"tree.h\"\n>  #include \"tree-walk.h\"\n>  #include \"unpack-trees.h\"\n> +#include \"wt-status.h\"\n>  #include \"xdiff-interface.h\"\n>  \n>  static const char * const checkout_usage[] = {\n> @@ -56,6 +57,7 @@ struct checkout_opts {\n>  \tint accept_pathspec;\n>  \tint switch_branch_doing_nothing_is_ok;\n>  \tint only_merge_on_switching_branches;\n> +\tint can_switch_when_in_progress;\n>  \n>  \tconst char *new_branch;\n>  \tconst char *new_branch_force;\n> @@ -1202,6 +1204,39 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)\n>  \tdie(_(\"a branch is expected, got '%s'\"), branch_info->name);\n>  }\n>  \n> +static void die_if_some_operation_in_progress(void)\n> +{\n> +\tstruct wt_status_state state;\n> +\n> +\tmemset(&state, 0, sizeof(state));\n> +\twt_status_get_state(the_repository, &state, 0);\n> +\n> +\tif (state.merge_in_progress)\n> +\t\tdie(_(\"cannot switch branch while merging\\n\"\n> +\t\t      \"Consider \\\"git merge --quit\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n\nI'm not sure merge --quit exists, 'git grep \\\"quit origin/pu' shows\nmatches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the\nsequencer command does not touch the index or working tree (that's the\ndifference between --quit and --abort) so the switch can still fail due\nchanges in the index and worktree that would be overwritten by the switch.\n\nBest Wishes\n\nPhillip\n\n> +\tif (state.am_in_progress)\n> +\t\tdie(_(\"cannot switch branch in the middle of an am session\\n\"\n> +\t\t      \"Consider \\\"git am --quit\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n> +\tif (state.rebase_interactive_in_progress || state.rebase_in_progress)\n> +\t\tdie(_(\"cannot switch branch while rebasing\\n\"\n> +\t\t      \"Consider \\\"git rebase --quit\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n> +\tif (state.cherry_pick_in_progress)\n> +\t\tdie(_(\"cannot switch branch while cherry-picking\\n\"\n> +\t\t      \"Consider \\\"git cherry-pick --quit\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n> +\tif (state.revert_in_progress)\n> +\t\tdie(_(\"cannot switch branch while reverting\\n\"\n> +\t\t      \"Consider \\\"git revert --quit\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n> +\tif (state.bisect_in_progress)\n> +\t\tdie(_(\"cannot switch branch while bisecting\\n\"\n> +\t\t      \"Consider \\\"git bisect reset HEAD\\\" \"\n> +\t\t      \"or \\\"git worktree add\\\".\"));\n> +}\n> +\n>  static int checkout_branch(struct checkout_opts *opts,\n>  \t\t\t   struct branch_info *new_branch_info)\n>  {\n> @@ -1257,6 +1292,9 @@ static int checkout_branch(struct checkout_opts *opts,\n>  \t    !new_branch_info->path)\n>  \t\tdie_expecting_a_branch(new_branch_info);\n>  \n> +\tif (!opts->can_switch_when_in_progress)\n> +\t\tdie_if_some_operation_in_progress();\n> +\n>  \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n>  \t    !opts->ignore_other_worktrees) {\n>  \t\tint flag;\n> @@ -1514,6 +1552,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \topts.only_merge_on_switching_branches = 0;\n>  \topts.accept_pathspec = 1;\n>  \topts.implicit_detach = 1;\n> +\topts.can_switch_when_in_progress = 1;\n>  \n>  \toptions = parse_options_dup(checkout_options);\n>  \toptions = add_common_options(&opts, options);\n> @@ -1549,6 +1588,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n>  \topts.switch_branch_doing_nothing_is_ok = 0;\n>  \topts.only_merge_on_switching_branches = 1;\n>  \topts.implicit_detach = 0;\n> +\topts.can_switch_when_in_progress = 0;\n>  \n>  \toptions = parse_options_dup(switch_options);\n>  \toptions = add_common_options(&opts, options);\n> \n\n"},{"id":"374633","messageId":"CACsJy8ASYRwUGV8VnVKLhBqdv9V+UTAhb6U3+4BUjJFnJ+2Viw@mail.gmail.com","threadId":"50360","inReplyTo":"78c7c281-82ec-2ba9-a607-dd2ecba54945@gmail.com","subject":"Re: [PATCH v6 23/27] switch: reject if some operation is in progress","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-04-29T09:16:01Z","receivedAt":"2019-04-29T09:16:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 25, 2019 at 5:33 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n> > Unless you know what you're doing, switching to another branch to do\n> > something then switching back could be confusing. Worse, you may even\n> > forget that you're in the middle of something. By the time you realize,\n> > you may have done a ton of work and it gets harder to go back.\n> >\n> > A new option --ignore-in-progress was considered but dropped because it\n> > was not exactly clear what should happen. Sometimes you can switch away\n> > and get back safely and resume the operation. Sometimes not. And the\n> > git-checkout behavior is automatically clear merge/revert/cherry-pick,\n> > which makes it a bit even more confusing [1].\n> >\n> > We may revisit and add this option in the future. But for now play it\n> > safe and not allow it (you can't even skip this check with --force).\n>\n> I think this is a good compromise, lets see how it goes (I think I\n> broadly agree with Elijah's suggestion to allow the switch if we can\n> safely switch back again if we want to add --ignore-in-progress in the\n> future).\n\nI probably will revisit this topic much sooner than I thought. I did a\nbisect today and found out \"git switch\" would not let me choose some\n\"random\" commit to test, which I suspected more likely where the\nproblem was, or at least helped reduce the bisect steps. I had to go\nback to \"git checkout\" and was not so happy.\n\nThis probably falls under the \"safe to switch\" (and not even back)\ncategory, as long as switching does not destroy any data, since bisect\nis basically jumping between commits with a clean worktree/index until\nyou find the right one.\n\n> > +static void die_if_some_operation_in_progress(void)\n> > +{\n> > +     struct wt_status_state state;\n> > +\n> > +     memset(&state, 0, sizeof(state));\n> > +     wt_status_get_state(the_repository, &state, 0);\n> > +\n> > +     if (state.merge_in_progress)\n> > +             die(_(\"cannot switch branch while merging\\n\"\n> > +                   \"Consider \\\"git merge --quit\\\" \"\n> > +                   \"or \\\"git worktree add\\\".\"));\n>\n> I'm not sure merge --quit exists, 'git grep \\\"quit origin/pu' shows\n> matches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the\n> sequencer command does not touch the index or working tree (that's the\n> difference between --quit and --abort) so the switch can still fail due\n> changes in the index and worktree that would be overwritten by the switch.\n\nEck! Let me check if --abort is the same thing there or we need to add --quit...\n-- \nDuy\n"},{"id":"374643","messageId":"38c4e7fb-0d32-7696-e2d0-83766027383d@gmail.com","threadId":"50360","inReplyTo":"CACsJy8ASYRwUGV8VnVKLhBqdv9V+UTAhb6U3+4BUjJFnJ+2Viw@mail.gmail.com","subject":"Re: [PATCH v6 23/27] switch: reject if some operation is in progress","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-29T15:10:53Z","receivedAt":"2019-04-29T15:11:00Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 29/04/2019 10:16, Duy Nguyen wrote:\n> On Thu, Apr 25, 2019 at 5:33 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n>>> Unless you know what you're doing, switching to another branch to do\n>>> something then switching back could be confusing. Worse, you may even\n>>> forget that you're in the middle of something. By the time you realize,\n>>> you may have done a ton of work and it gets harder to go back.\n>>>\n>>> A new option --ignore-in-progress was considered but dropped because it\n>>> was not exactly clear what should happen. Sometimes you can switch away\n>>> and get back safely and resume the operation. Sometimes not. And the\n>>> git-checkout behavior is automatically clear merge/revert/cherry-pick,\n>>> which makes it a bit even more confusing [1].\n>>>\n>>> We may revisit and add this option in the future. But for now play it\n>>> safe and not allow it (you can't even skip this check with --force).\n>>\n>> I think this is a good compromise, lets see how it goes (I think I\n>> broadly agree with Elijah's suggestion to allow the switch if we can\n>> safely switch back again if we want to add --ignore-in-progress in the\n>> future).\n> \n> I probably will revisit this topic much sooner than I thought. I did a\n> bisect today and found out \"git switch\" would not let me choose some\n> \"random\" commit to test, which I suspected more likely where the\n> problem was, or at least helped reduce the bisect steps. I had to go\n> back to \"git checkout\" and was not so happy.\n\nOh that's a pain, but should be safe as you describe below.\n\n> This probably falls under the \"safe to switch\" (and not even back)\n> category, as long as switching does not destroy any data, since bisect\n> is basically jumping between commits with a clean worktree/index until\n> you find the right one. >>> +static void die_if_some_operation_in_progress(void)\n>>> +{\n>>> +     struct wt_status_state state;\n>>> +\n>>> +     memset(&state, 0, sizeof(state));\n>>> +     wt_status_get_state(the_repository, &state, 0);\n>>> +\n>>> +     if (state.merge_in_progress)\n>>> +             die(_(\"cannot switch branch while merging\\n\"\n>>> +                   \"Consider \\\"git merge --quit\\\" \"\n>>> +                   \"or \\\"git worktree add\\\".\"));\n>>\n>> I'm not sure merge --quit exists, 'git grep \\\"quit origin/pu' shows\n>> matches for builtin/{am.c,rebase.c,revert.c}. The --quit option for the\n>> sequencer command does not touch the index or working tree (that's the\n>> difference between --quit and --abort) so the switch can still fail due\n>> changes in the index and worktree that would be overwritten by the switch.\n> \n> Eck! Let me check if --abort is the same thing there or we need to add --quit...\n\nI think the --quit options generally leave the index and worktree alone, \nthey just remove the state files whereas abort resets the index and \nworktree (with reset --mixed for merge and cherry-pick/revert, I think \nrebase does reset --hard) and also rewinds HEAD for rebases and \nsequences of cherry-picks and revert but that wont apply to merges as \nthey are one-shot operations.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"374644","messageId":"9848d618-04f6-2472-f267-5926a030f5f2@gmail.com","threadId":"50360","inReplyTo":"CACsJy8BF5DwykEmmPGDR7+AzhOAFreJnML073K0xNBdTDjqMJg@mail.gmail.com","subject":"Re: [PATCH v6 15/27] switch: add --discard-changes","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-29T15:14:51Z","receivedAt":"2019-04-29T15:14:57Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 25/04/2019 11:12, Duy Nguyen wrote:\n> On Thu, Apr 25, 2019 at 5:02 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 29/03/2019 10:39, Nguyễn Thái Ngọc Duy wrote:\n>>> --discard-changes is a better name than --force for this option since\n>>> it's what really happens.\n>>\n>> I didn't realize when I suggested the name that --force overwrites\n>> untracked files as well as discarding changes from tracked files. I\n>> think we should document that. It would be nice if read-tree --reset -u\n>> took an optional argument so read-tree --reset=tracked -u would not\n>> overwrite untracked files. Then we could have --discard-changes just\n>> discard the changes and not overwrite untracked files. I had a quick\n>> look at unpack trees and it looks like a fairly straight forward change\n>> (famous last words) - perhaps I'll have a go at it next week.\n> \n> So, --discard-changes is all about tracked changes, and we may have\n> --overwrite-untracked to cover the other part, and --force enables\n> both? \n\nI was thinking of --discard-changes dealing with tracked changes and \nhaving --force for untracked changes as well. I'm not sure we need \n--overwrite-untracked for switch, just for read-tree.\n\nThat does not sound so bad (and maybe a good cure for those\n> \"overwriting untracked\" reports we've seen quite often lately).\n> \n> Good luck with unpack-trees.c. But if it turns out you're too busy,\n> just let me know if want to hand that back to me.\n\nThanks, I've got something working, I'll clean it up and send it later \nin the week.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"378475","messageId":"20190702080611.GL21574@szeder.dev","threadId":"50360","inReplyTo":"20190329103919.15642-8-pclouds@gmail.com","subject":"Re: [PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-07-02T08:06:11Z","receivedAt":"2019-07-02T08:06:22Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> After a successful switch, if a merge, cherry-pick or revert is ongoing,\n> it is canceled. This behavior has been with us from the very early\n> beginning, soon after git-merge was created but never actually\n> documented [1]. It may be a good idea to be transparent and tell the\n> user if some operation is canceled.\n\nAfter this entered 'next' last week, today it greeted me with 167(!)\nof these warnings...  before I even had my breakfast.\n\nNow, my script does a lot of repeated cherry-picks and expects that\nrerere is able to deal with most of the conflicts, i.e. it does\napproximately this:\n\n  if ! git cherry-pick $oid >/dev/null 2>&1\n  then\n      if was_the_conflict_resolved\n      then\n          echo \"using previous conflict resolution\"\n          git commit --no-edit --cleanup=strip --quiet\n      else\n          die \"uh-oh\"\n      fi\n  fi\n\nThat 'git commit' in there always prints:\n\n  warning: cancelling a cherry picking in progress\n\nI don't understand why committing after a cherry-pick is considered\n\"cancelling\"...  in my view it's finishing it and there should be no\nwarning whatsoever.\n\n> I consider this a better way of telling the user than just adding a\n> sentence or two in git-checkout.txt, which will be mostly ignored\n> anyway.\n> \n> PS. Originally I wanted to print more details like\n> \n>     warning: cancelling an in-progress merge from <SHA-1>\n> \n> which may allow some level of undo if the user wants to. But that seems\n> a lot more work. Perhaps it can be improved later if people still want\n> that.\n> \n> [1] ... and I will try not to argue whether it is a sensible behavior.\n> There is some more discussion here if people are interested:\n> CACsJy8Axa5WsLSjiscjnxVK6jQHkfs-gH959=YtUvQkWriAk5w@mail.gmail.com\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  branch.c           | 11 +++++++----\n>  branch.h           |  2 +-\n>  builtin/am.c       |  2 +-\n>  builtin/checkout.c |  2 +-\n>  builtin/rebase.c   |  4 ++--\n>  builtin/reset.c    |  2 +-\n>  builtin/revert.c   |  2 +-\n>  7 files changed, 14 insertions(+), 11 deletions(-)\n> \n> diff --git a/branch.c b/branch.c\n> index 28b81a7e02..8dd5bb9f1c 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -337,11 +337,14 @@ void create_branch(struct repository *r,\n>  \tfree(real_ref);\n>  }\n>  \n> -void remove_branch_state(struct repository *r)\n> +void remove_branch_state(struct repository *r, int verbose)\n>  {\n> -\tunlink(git_path_cherry_pick_head(r));\n> -\tunlink(git_path_revert_head(r));\n> -\tunlink(git_path_merge_head(r));\n> +\tif (!unlink(git_path_cherry_pick_head(r)) && verbose)\n> +\t\twarning(_(\"cancelling a cherry picking in progress\"));\n> +\tif (!unlink(git_path_revert_head(r)) && verbose)\n> +\t\twarning(_(\"cancelling a revert in progress\"));\n> +\tif (!unlink(git_path_merge_head(r)) && verbose)\n> +\t\twarning(_(\"cancelling a merge in progress\"));\n>  \tunlink(git_path_merge_rr(r));\n>  \tunlink(git_path_merge_msg(r));\n>  \tunlink(git_path_merge_mode(r));\n> diff --git a/branch.h b/branch.h\n> index 29c1afa4d0..aed045901e 100644\n> --- a/branch.h\n> +++ b/branch.h\n> @@ -64,7 +64,7 @@ extern int validate_new_branchname(const char *name, struct strbuf *ref, int for\n>   * Remove information about the state of working on the current\n>   * branch. (E.g., MERGE_HEAD)\n>   */\n> -void remove_branch_state(struct repository *r);\n> +void remove_branch_state(struct repository *r, int verbose);\n>  \n>  /*\n>   * Configure local branch \"local\" as downstream to branch \"remote\"\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 4fb107a9d1..99b66508fd 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -1957,7 +1957,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem\n>  \tif (merge_tree(remote_tree))\n>  \t\treturn -1;\n>  \n> -\tremove_branch_state(the_repository);\n> +\tremove_branch_state(the_repository, 0);\n>  \n>  \treturn 0;\n>  }\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 0e6037b296..f66bd2f56d 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -899,7 +899,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n>  \t\t\t\tdelete_reflog(old_branch_info->path);\n>  \t\t}\n>  \t}\n> -\tremove_branch_state(the_repository);\n> +\tremove_branch_state(the_repository, !opts->quiet);\n>  \tstrbuf_release(&msg);\n>  \tif (!opts->quiet &&\n>  \t    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, \"HEAD\"))))\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 52114cbf0d..646d0f9fb1 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -1272,7 +1272,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n>  \t\t\t       NULL, NULL) < 0)\n>  \t\t\tdie(_(\"could not discard worktree changes\"));\n> -\t\tremove_branch_state(the_repository);\n> +\t\tremove_branch_state(the_repository, 0);\n>  \t\tif (read_basic_state(&options))\n>  \t\t\texit(1);\n>  \t\tgoto run_rebase;\n> @@ -1292,7 +1292,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\t\t       NULL, NULL) < 0)\n>  \t\t\tdie(_(\"could not move back to %s\"),\n>  \t\t\t    oid_to_hex(&options.orig_head));\n> -\t\tremove_branch_state(the_repository);\n> +\t\tremove_branch_state(the_repository, 0);\n>  \t\tret = finish_rebase(&options);\n>  \t\tgoto cleanup;\n>  \t}\n> diff --git a/builtin/reset.c b/builtin/reset.c\n> index 7882829a95..6d9397c844 100644\n> --- a/builtin/reset.c\n> +++ b/builtin/reset.c\n> @@ -420,7 +420,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>  \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>  \t}\n>  \tif (!pathspec.nr)\n> -\t\tremove_branch_state(the_repository);\n> +\t\tremove_branch_state(the_repository, 0);\n>  \n>  \treturn update_ref_status;\n>  }\n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index a47b53ceaf..ebf2789225 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -196,7 +196,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n>  \tif (cmd == 'q') {\n>  \t\tint ret = sequencer_remove_state(opts);\n>  \t\tif (!ret)\n> -\t\t\tremove_branch_state(the_repository);\n> +\t\t\tremove_branch_state(the_repository, 0);\n>  \t\treturn ret;\n>  \t}\n>  \tif (cmd == 'c')\n> -- \n> 2.21.0.479.g47ac719cd3\n> \n"},{"id":"378476","messageId":"20190702090308.GA27775@ash","threadId":"50360","inReplyTo":"20190702080611.GL21574@szeder.dev","subject":"Re: [PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-07-02T09:03:09Z","receivedAt":"2019-07-02T09:03:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jul 02, 2019 at 10:06:11AM +0200, SZEDER Gábor wrote:\n> On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > After a successful switch, if a merge, cherry-pick or revert is ongoing,\n> > it is canceled. This behavior has been with us from the very early\n> > beginning, soon after git-merge was created but never actually\n> > documented [1]. It may be a good idea to be transparent and tell the\n> > user if some operation is canceled.\n> \n> After this entered 'next' last week, today it greeted me with 167(!)\n> of these warnings...  before I even had my breakfast.\n> \n> Now, my script does a lot of repeated cherry-picks and expects that\n> rerere is able to deal with most of the conflicts, i.e. it does\n> approximately this:\n> \n>   if ! git cherry-pick $oid >/dev/null 2>&1\n>   then\n>       if was_the_conflict_resolved\n>       then\n>           echo \"using previous conflict resolution\"\n>           git commit --no-edit --cleanup=strip --quiet\n>       else\n>           die \"uh-oh\"\n>       fi\n>   fi\n> \n> That 'git commit' in there always prints:\n> \n>   warning: cancelling a cherry picking in progress\n> \n> I don't understand why committing after a cherry-pick is considered\n> \"cancelling\"...  in my view it's finishing it and there should be no\n> warning whatsoever.\n> \n\nI agree, this is not \"canceling\". I think this series causes conflicts\nwith pw/clean-sequencer-state-upon-final-commit and the warning is\naccidentally enabled (partly my fault since I named the argument\n\"verbose\").\n\nJunio, in this conflict resolution (merging nd/switch-and-restore to\nnext), we should pass '0' instead of 'verbose' to\nsequencer_post_commit_cleanup().\n\ndiff --cc builtin/commit.c\nindex 1921401117,fa5982cc86..145d50caf0\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a\n  \t\tdie(\"%s\", err.buf);\n  \t}\n  \n- \tsequencer_post_commit_cleanup(the_repository);\n -\tunlink(git_path_cherry_pick_head(the_repository));\n -\tunlink(git_path_revert_head(the_repository));\n++\tsequencer_post_commit_cleanup(the_repository, verbose);\n  \tunlink(git_path_merge_head(the_repository));\n  \tunlink(git_path_merge_msg(the_repository));\n  \tunlink(git_path_merge_mode(the_repository));\n--\nDuy\n"},{"id":"378485","messageId":"20190702104303.GM21574@szeder.dev","threadId":"50360","inReplyTo":"20190702090308.GA27775@ash","subject":"Re: [PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-07-02T10:43:03Z","receivedAt":"2019-07-02T10:43:10Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Jul 02, 2019 at 04:03:09PM +0700, Duy Nguyen wrote:\n> On Tue, Jul 02, 2019 at 10:06:11AM +0200, SZEDER Gábor wrote:\n> > On Fri, Mar 29, 2019 at 05:38:59PM +0700, Nguyễn Thái Ngọc Duy wrote:\n> > > After a successful switch, if a merge, cherry-pick or revert is ongoing,\n> > > it is canceled. This behavior has been with us from the very early\n> > > beginning, soon after git-merge was created but never actually\n> > > documented [1]. It may be a good idea to be transparent and tell the\n> > > user if some operation is canceled.\n> > \n> > After this entered 'next' last week, today it greeted me with 167(!)\n> > of these warnings...  before I even had my breakfast.\n> > \n> > Now, my script does a lot of repeated cherry-picks and expects that\n> > rerere is able to deal with most of the conflicts, i.e. it does\n> > approximately this:\n> > \n> >   if ! git cherry-pick $oid >/dev/null 2>&1\n> >   then\n> >       if was_the_conflict_resolved\n> >       then\n> >           echo \"using previous conflict resolution\"\n> >           git commit --no-edit --cleanup=strip --quiet\n> >       else\n> >           die \"uh-oh\"\n> >       fi\n> >   fi\n> > \n> > That 'git commit' in there always prints:\n> > \n> >   warning: cancelling a cherry picking in progress\n> > \n> > I don't understand why committing after a cherry-pick is considered\n> > \"cancelling\"...  in my view it's finishing it and there should be no\n> > warning whatsoever.\n> > \n> \n> I agree, this is not \"canceling\". I think this series causes conflicts\n> with pw/clean-sequencer-state-upon-final-commit and the warning is\n> accidentally enabled (partly my fault since I named the argument\n> \"verbose\").\n> \n> Junio, in this conflict resolution (merging nd/switch-and-restore to\n> next), we should pass '0' instead of 'verbose' to\n> sequencer_post_commit_cleanup().\n> \n> diff --cc builtin/commit.c\n> index 1921401117,fa5982cc86..145d50caf0\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a\n>   \t\tdie(\"%s\", err.buf);\n>   \t}\n>   \n> - \tsequencer_post_commit_cleanup(the_repository);\n>  -\tunlink(git_path_cherry_pick_head(the_repository));\n>  -\tunlink(git_path_revert_head(the_repository));\n> ++\tsequencer_post_commit_cleanup(the_repository, verbose);\n>   \tunlink(git_path_merge_head(the_repository));\n>   \tunlink(git_path_merge_msg(the_repository));\n>   \tunlink(git_path_merge_mode(the_repository));\n\nThanks, this indeed takes care of it.\n\n"},{"id":"378507","messageId":"xmqqa7dw4966.fsf@gitster-ct.c.googlers.com","threadId":"50360","inReplyTo":"20190702090308.GA27775@ash","subject":"Re: [PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-02T17:51:13Z","receivedAt":"2019-07-02T17:51:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I agree, this is not \"canceling\". I think this series causes conflicts\n> with pw/clean-sequencer-state-upon-final-commit and the warning is\n> accidentally enabled (partly my fault since I named the argument\n> \"verbose\").\n>\n> Junio, in this conflict resolution (merging nd/switch-and-restore to\n> next), we should pass '0' instead of 'verbose' to\n> sequencer_post_commit_cleanup().\n\nThanks for an update to the merge-fix.  What should the name of the\nparameter to the sequencer_post_commit_cleanup() funcion be then,\nthough?  Perhaps we'd want an update after the topic graduates to\nthe 'master' branch.\n\n>\n> diff --cc builtin/commit.c\n> index 1921401117,fa5982cc86..145d50caf0\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@@ -1658,7 -1666,8 +1658,7 @@@ int cmd_commit(int argc, const char **a\n>   \t\tdie(\"%s\", err.buf);\n>   \t}\n>   \n> - \tsequencer_post_commit_cleanup(the_repository);\n>  -\tunlink(git_path_cherry_pick_head(the_repository));\n>  -\tunlink(git_path_revert_head(the_repository));\n> ++\tsequencer_post_commit_cleanup(the_repository, verbose);\n>   \tunlink(git_path_merge_head(the_repository));\n>   \tunlink(git_path_merge_msg(the_repository));\n>   \tunlink(git_path_merge_mode(the_repository));\n> --\n> Duy\n"},{"id":"378614","messageId":"CACsJy8Agbvn0OYiM8z_r6PAJW0Tov9eQyN3NviD8rLQKNdc45A@mail.gmail.com","threadId":"50360","inReplyTo":"xmqqa7dw4966.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v6 07/27] checkout: inform the user when removing branch state","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-07-04T23:57:03Z","receivedAt":"2019-07-04T23:57:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Jul 3, 2019 at 12:51 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> > I agree, this is not \"canceling\". I think this series causes conflicts\n> > with pw/clean-sequencer-state-upon-final-commit and the warning is\n> > accidentally enabled (partly my fault since I named the argument\n> > \"verbose\").\n> >\n> > Junio, in this conflict resolution (merging nd/switch-and-restore to\n> > next), we should pass '0' instead of 'verbose' to\n> > sequencer_post_commit_cleanup().\n>\n> Thanks for an update to the merge-fix.  What should the name of the\n> parameter to the sequencer_post_commit_cleanup() funcion be then,\n> though?  Perhaps we'd want an update after the topic graduates to\n> the 'master' branch.\n\nDefinitely. I was thinking something with \"warn\" and \"cancel\", which\nis more the the point. Probably warn_cancel_in_progress, or something.\n-- \nDuy\n"}]}