{"thread":{"id":"65779","subject":"[PATCH] switch: add --ensure option","startedAt":"2026-06-09T09:23:50Z","lastAt":"2026-06-17T10:57:11Z","messageCount":3,"participants":["Lei Zhu via GitGitGadget","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"545041","messageId":"pull.2324.git.git.1780997009796.gitgitgadget@gmail.com","threadId":"65779","inReplyTo":null,"subject":"[PATCH] switch: add --ensure option","fromName":"Lei Zhu via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-06-09T09:23:29Z","receivedAt":"2026-06-09T09:23:50Z","isPatch":true,"body":"From: Korov <korov9.c@gmail.com>\n\nAdd a new `git switch --ensure` (`-e`) option that behaves like an\nidempotent form of branch switching.\n\nUsers who often switch between topic branches may not know whether the\nlocal branch already exists. Without this option, they need to check\nfor the branch first and then choose between `git switch <branch>` and\n`git switch -c <branch>`. The new option folds that workflow into a\nsingle command.\n\nWhen the target branch does not exist, `git switch -e <branch>`\nbehaves like `git switch -c <branch>`, including existing `--track`\nand `--no-track` handling.\n\nWhen the target branch already exists, `git switch -e <branch>`\nswitches to it without resetting the branch tip. If `--track` is\ngiven, update the branch's upstream configuration using the explicit\nstart-point, or the current branch when no start-point is provided.\nFail in detached HEAD state when no start-point is available for\ntracking setup.\n\nDocument the new option and add tests covering create-branch tracking,\nexisting-branch tracking updates, and detached-HEAD failure cases.\n\nSigned-off-by: Korov <korov9.c@gmail.com>\n---\n    switch: add --ensure option\n    \n    Add a new git switch --ensure (-e) option that behaves like an\n    idempotent form of branch switching.\n    \n    Users who often switch between topic branches may not know whether the\n    local branch already exists. Without this option, they need to check for\n    the branch first and then choose between git switch <branch> and git\n    switch -c <branch>. The new option folds that workflow into a single\n    command.\n    \n    When the target branch does not exist, git switch -e <branch> behaves\n    like git switch -c <branch>, including existing --track and --no-track\n    handling.\n    \n    When the target branch already exists, git switch -e <branch> switches\n    to it without resetting the branch tip. If --track is given, update the\n    branch's upstream configuration using the explicit start-point, or the\n    current branch when no start-point is provided. Fail in detached HEAD\n    state when no start-point is available for tracking setup.\n    \n    Document the new option and add tests covering create-branch tracking,\n    existing-branch tracking updates, and detached-HEAD failure cases.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2324%2FKorov%2Fdev3-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2324/Korov/dev3-v1\nPull-Request: https://github.com/git/git/pull/2324\n\n Documentation/git-switch.adoc | 16 +++++++\n builtin/checkout.c            | 86 ++++++++++++++++++++++++++++++++++-\n t/t2060-switch.sh             | 46 +++++++++++++++++++\n 3 files changed, 146 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc\nindex d6c4f229a5..a0ac31fa23 100644\n--- a/Documentation/git-switch.adoc\n+++ b/Documentation/git-switch.adoc\n@@ -11,6 +11,7 @@ SYNOPSIS\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>] -e <branch> [<start-point>]\n git switch [<options>] --orphan <new-branch>\n \n DESCRIPTION\n@@ -81,6 +82,21 @@ $ git branch -f _<new-branch>_\n $ git switch _<new-branch>_\n ------------\n \n+`-e <branch>`::\n+`--ensure <branch>`::\n+\tSwitch to _<branch>_ if it already exists, or create it from\n+\t_<start-point>_ before switching to it if it does not.\n++\n+When _<branch>_ does not already exist, this behaves like\n+`git switch -c <branch> [<start-point>]`, including any `--track`\n+or `--no-track` options.\n++\n+When _<branch>_ already exists, the branch tip is not changed. If\n+`--track[=(direct|inherit)]` is given, the existing branch's upstream\n+configuration is updated using _<start-point>_ when one is provided,\n+or the current branch when _<start-point>_ is omitted. This form fails\n+when `HEAD` is detached and no _<start-point>_ is given.\n+\n `-d`::\n `--detach`::\n \tSwitch to a commit for inspection and discardable\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b78b3a1d16..f56935bfe2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -81,6 +81,8 @@ struct checkout_opts {\n \tconst char *new_branch;\n \tconst char *new_branch_force;\n \tconst char *new_orphan_branch;\n+\tconst char *ensure_branch;\n+\tconst char *ensure_branch_start;\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n@@ -988,6 +990,15 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\tfree(new_branch_info->refname);\n \t\tnew_branch_info->name = xstrdup(opts->new_branch);\n \t\tsetup_branch_path(new_branch_info);\n+\t} else if (opts->ensure_branch && opts->branch_exists &&\n+\t\t   opts->track != BRANCH_TRACK_UNSPECIFIED) {\n+\t\tconst char *tracking_source = opts->ensure_branch_start ?\n+\t\t\topts->ensure_branch_start :\n+\t\t\told_branch_info->name;\n+\t\tdwim_and_setup_tracking(the_repository, opts->ensure_branch,\n+\t\t\t\t\ttracking_source, opts->track,\n+\t\t\t\t\topts->quiet);\n+\t\tremote_state_clear(the_repository->remote_state);\n \t}\n \n \told_desc = old_branch_info->name;\n@@ -1927,6 +1938,52 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tdie(_(\"options '-%c', '-%c', and '%s' cannot be used together\"),\n \t\t\tcb_option, toupper(cb_option), \"--orphan\");\n \n+\tif (opts->ensure_branch) {\n+\t\tstruct strbuf ref = STRBUF_INIT;\n+\t\tint exists;\n+\n+\t\tif (opts->new_branch || opts->new_branch_force || opts->new_orphan_branch)\n+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-e\", \"-c/-C/--orphan\");\n+\t\tif (opts->force_detach)\n+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-e\", \"--detach\");\n+\n+\t\texists = validate_branchname(opts->ensure_branch, &ref);\n+\t\tstrbuf_release(&ref);\n+\n+\t\t/* Save an explicit start point for tracking setup. */\n+\t\tif (argc > 0 && opts->track != BRANCH_TRACK_UNSPECIFIED)\n+\t\t\topts->ensure_branch_start = argv[0];\n+\n+\t\tif (exists) {\n+\t\t\t/*\n+\t\t\t * Branch exists: just switch to it, don't reset.\n+\t\t\t * We'll set up tracking after the switch if --track was given.\n+\t\t\t */\n+\t\t\topts->branch_exists = 1;\n+\t\t} else {\n+\t\t\t/* Branch doesn't exist: create it like -c */\n+\t\t\topts->new_branch = opts->ensure_branch;\n+\t\t}\n+\t}\n+\n+\tif (opts->ensure_branch && opts->branch_exists &&\n+\t    opts->track != BRANCH_TRACK_UNSPECIFIED &&\n+\t    !opts->ensure_branch_start) {\n+\t\tstruct object_id head_oid;\n+\t\tchar *head = refs_resolve_refdup(get_main_ref_store(the_repository),\n+\t\t\t\t\t\t \"HEAD\", 0, &head_oid, NULL);\n+\t\tconst char *branch;\n+\n+\t\tif (!head)\n+\t\t\tdie(_(\"failed to resolve HEAD as a valid ref\"));\n+\t\tif (!strcmp(head, \"HEAD\"))\n+\t\t\tdie(_(\"cannot set up tracking information; starting point '%s' is not a branch\"),\n+\t\t\t    \"HEAD\");\n+\t\tif (!skip_prefix(head, \"refs/heads/\", &branch))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t\tfree(head);\n+\t}\n+\n \tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-p\", \"--overlay\");\n \n@@ -1961,8 +2018,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->new_orphan_branch)\n \t\topts->new_branch = opts->new_orphan_branch;\n \n-\t/* --track without -c/-C/-b/-B/--orphan should DWIM */\n-\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n+\t/* --track without -c/-C/-b/-B/--orphan/-e should DWIM */\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch &&\n+\t    !(opts->ensure_branch && opts->branch_exists)) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -2012,6 +2070,28 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\t/*\n+\t * Handle -e with existing branch: set up new_branch_info to switch\n+\t * to the existing branch.\n+\t */\n+\tif (opts->ensure_branch && opts->branch_exists) {\n+\t\tstruct object_id rev;\n+\n+\t\tbranch_info_release(&new_branch_info);\n+\t\tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n+\t\tnew_branch_info.name = xstrdup(opts->ensure_branch);\n+\t\tsetup_branch_path(&new_branch_info);\n+\n+\t\tif (new_branch_info.path &&\n+\t\t    !refs_read_ref(get_main_ref_store(the_repository),\n+\t\t\t\t   new_branch_info.path, &rev)) {\n+\t\t\tnew_branch_info.commit = lookup_commit_reference_gently(\n+\t\t\t\tthe_repository, &rev, 1);\n+\t\t\tif (new_branch_info.commit)\n+\t\t\t\tparse_commit_or_die(new_branch_info.commit);\n+\t\t}\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@@ -2150,6 +2230,8 @@ int cmd_switch(int argc,\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_STRING('e', \"ensure\", &opts.ensure_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create if needed and switch to 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,\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nindex c91c4db936..c0bff7caab 100755\n--- a/t/t2060-switch.sh\n+++ b/t/t2060-switch.sh\n@@ -146,6 +146,52 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '\n \ttest_cmp_config \"\" --default \"\" branch.main2.merge\n '\n \n+test_expect_success 'switch -e --track creates branch from current branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-new-current || :\n+\t\" &&\n+\tgit switch main &&\n+\tgit switch -e ensure-new-current --track &&\n+\ttest_cmp_rev refs/heads/main refs/heads/ensure-new-current &&\n+\ttest_cmp_config . branch.ensure-new-current.remote &&\n+\ttest_cmp_config refs/heads/main branch.ensure-new-current.merge\n+'\n+\n+test_expect_success 'switch -e --track creates branch from remote-tracking branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-new || :\n+\t\" &&\n+\tgit switch -e ensure-new --track origin/foo &&\n+\ttest_cmp_rev refs/remotes/origin/foo refs/heads/ensure-new &&\n+\ttest_cmp_config origin branch.ensure-new.remote &&\n+\ttest_cmp_config refs/heads/foo branch.ensure-new.merge\n+'\n+\n+test_expect_success 'switch -e --track uses current branch for existing branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-existing source-for-track || :\n+\t\" &&\n+\tgit switch -c source-for-track main &&\n+\tgit branch ensure-existing main &&\n+\tgit switch -e ensure-existing --track &&\n+\ttest_cmp_config . branch.ensure-existing.remote &&\n+\ttest_cmp_config refs/heads/source-for-track branch.ensure-existing.merge\n+'\n+\n+test_expect_success 'switch -e --track fails from detached HEAD without start-point' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D detached-target || :\n+\t\" &&\n+\tgit branch detached-target main &&\n+\tgit switch --detach main &&\n+\ttest_must_fail git switch -e detached-target --track 2>stderr &&\n+\ttest_grep \"cannot set up tracking information; starting point '\\''HEAD'\\'' is not a branch\" stderr\n+'\n+\n test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '\n \ttest_when_finished \"\n \t\tgit worktree remove wt1 ||:\n\nbase-commit: 600fe743028cbfb640855f659e9851522214bc0b\n-- \ngitgitgadget\n"},{"id":"545067","messageId":"xmqq33yvuax0.fsf@gitster.g","threadId":"65779","inReplyTo":"pull.2324.git.git.1780997009796.gitgitgadget@gmail.com","subject":"Re: [PATCH] switch: add --ensure option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-09T12:59:39Z","receivedAt":"2026-06-09T12:59:41Z","isPatch":true,"body":"\"Lei Zhu via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Users who often switch between topic branches may not know whether the\n> local branch already exists. Without this option, they need to check\n> for the branch first and then choose between `git switch <branch>` and\n> `git switch -c <branch>`. The new option folds that workflow into a\n> single command.\n\nQuite honestly, I am not sure if the use case should be supported\nwith a new option, or we should actively discourage it by rejecting\nany patch that takes us in this direction, as the actions the user\nwould take after seeing the result of \"git checkout -b\" or \"git\nswitch -c\" are quite different among (just off the top of my head):\n\n (1) Ah we already had the branch created exactly to work on this;\n     instead of forking a new effort, switch to the existing branch\n     and build on the effort we made previously, as it forks from an\n     acceptable base, which might have been different from where we\n     wanted to start at when we said \"git switch -c <branch> <base>\".\n\n (2) Ah we already had the branch created exactly to work on this.\n     Unfortunately, it was forked from way too new base before we\n     realized that this is also an important bugfix that needs to be\n     mergeable to the maintenance track.  Let's create a new branch\n     that is a copy of the existing one with \"-maint\" in its name,\n     rebase it on the maintenance track, and work there.\n\n (3) Ah we already had a branch that happened to have the same name,\n     but created for totally different reasons.  We do want to fork\n     a new branch but need to give it a different name.\n\n (4) There wasn't a branch with the given name, so we created a new\n     branch at the right starting point we just picked when we ran\n     \"git checkout -b\"/\"git switch -c\".  Let's start working on the\n     topic.\n\nYou cover *only* case (4) perfectly.  When your \"-c <branch>\" picks\nan existing branch, the user still needs to figure out which among\nsituations (1)-(3) (of course, there may be others) they are in, and\nact accordingly.  \"git checkout -b\" and \"git switch -c\" that fails,\nreminding that there is an existing branch with the same name, gives\nusers a stronger form of reminder than switching blindly to the\nexisting branch, which may (in case (1)) or may not (in cases (2)\nand (3)) be where the user wants to be when taking the next action.\n\nHaving said that.\n\n * The option name \"-e\" would make all users expect that this has\n   something to do with \"--editor\".  Start with a longer name,\n   perhaps \"--create-if-missing\" or something, and then see if\n   others can come up with a better short-hand.  Obviously whoever\n   chooses \"-e\" is not equipped well to do so (yet), and the\n   reviewer who pointed out \"-e\" is not a good idea without being\n   able to offer a better alternative is not, either ;-).\n\n * Adding a new flag only to \"switch\" without \"checkout\" will\n   unnecessary confuse users.  This is because, even though\n   \"switch/restore\" started as an experiment to _supersede_\n   \"checkout\", they were not successful, not in the sense that\n   \"switch/restore\" were harder to use than the original, but in the\n   sense that the userbase and teaching materials are already used\n   to the original and removing it is practically infeasible.\n"},{"id":"545755","messageId":"pull.2324.v2.git.git.1781693828456.gitgitgadget@gmail.com","threadId":"65779","inReplyTo":"pull.2324.git.git.1780997009796.gitgitgadget@gmail.com","subject":"[PATCH v2] checkout/switch: add --create-if-missing option","fromName":"Lei Zhu via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-06-17T10:57:08Z","receivedAt":"2026-06-17T10:57:11Z","isPatch":true,"body":"From: Korov <korov9.c@gmail.com>\n\nAdd a new `--create-if-missing` option to `git switch` and `git\ncheckout` that behaves like an idempotent form of branch switching.\n\nUsers who often switch between topic branches may not know whether the\nlocal branch already exists. Without this option, they need to check for\nthe branch first and then choose between switching to it or creating it.\nThe new option folds that workflow into a single command.\n\nWhen the target branch does not exist, `--create-if-missing <branch>`\nbehaves like `git switch -c <branch>` or `git checkout -b <branch>`,\nincluding existing `--track` and `--no-track` handling.\n\nWhen the target branch already exists, `--create-if-missing <branch>`\nswitches to it without resetting the branch tip. If `--track` is given,\nupdate the branch's upstream configuration using the explicit\nstart-point, or the current branch when no start-point is provided. Fail\nin detached HEAD state when no start-point is available for tracking\nsetup.\n\nFor `git checkout`, keep this as a branch operation and reject pathspec\nusage with `--create-if-missing` to avoid mixing branch switching with\npath checkout semantics.\n\nDocument the new option and add tests covering branch creation,\nexisting-branch switching, tracking updates, pathspec rejection, and\ndetached-HEAD failure cases.\n\nSigned-off-by: Korov <korov9.c@gmail.com>\n---\n    checkout/switch: add --create-if-missing option\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2324%2FKorov%2Fdev3-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2324/Korov/dev3-v2\nPull-Request: https://github.com/git/git/pull/2324\n\nRange-diff vs v1:\n\n 1:  64a6947ad1 ! 1:  0070592d49 switch: add --ensure option\n     @@ Metadata\n      Author: Korov <korov9.c@gmail.com>\n      \n       ## Commit message ##\n     -    switch: add --ensure option\n     +    checkout/switch: add --create-if-missing option\n      \n     -    Add a new `git switch --ensure` (`-e`) option that behaves like an\n     -    idempotent form of branch switching.\n     +    Add a new `--create-if-missing` option to `git switch` and `git\n     +    checkout` that behaves like an idempotent form of branch switching.\n      \n          Users who often switch between topic branches may not know whether the\n     -    local branch already exists. Without this option, they need to check\n     -    for the branch first and then choose between `git switch <branch>` and\n     -    `git switch -c <branch>`. The new option folds that workflow into a\n     -    single command.\n     +    local branch already exists. Without this option, they need to check for\n     +    the branch first and then choose between switching to it or creating it.\n     +    The new option folds that workflow into a single command.\n      \n     -    When the target branch does not exist, `git switch -e <branch>`\n     -    behaves like `git switch -c <branch>`, including existing `--track`\n     -    and `--no-track` handling.\n     +    When the target branch does not exist, `--create-if-missing <branch>`\n     +    behaves like `git switch -c <branch>` or `git checkout -b <branch>`,\n     +    including existing `--track` and `--no-track` handling.\n      \n     -    When the target branch already exists, `git switch -e <branch>`\n     -    switches to it without resetting the branch tip. If `--track` is\n     -    given, update the branch's upstream configuration using the explicit\n     -    start-point, or the current branch when no start-point is provided.\n     -    Fail in detached HEAD state when no start-point is available for\n     -    tracking setup.\n     +    When the target branch already exists, `--create-if-missing <branch>`\n     +    switches to it without resetting the branch tip. If `--track` is given,\n     +    update the branch's upstream configuration using the explicit\n     +    start-point, or the current branch when no start-point is provided. Fail\n     +    in detached HEAD state when no start-point is available for tracking\n     +    setup.\n      \n     -    Document the new option and add tests covering create-branch tracking,\n     -    existing-branch tracking updates, and detached-HEAD failure cases.\n     +    For `git checkout`, keep this as a branch operation and reject pathspec\n     +    usage with `--create-if-missing` to avoid mixing branch switching with\n     +    path checkout semantics.\n     +\n     +    Document the new option and add tests covering branch creation,\n     +    existing-branch switching, tracking updates, pathspec rejection, and\n     +    detached-HEAD failure cases.\n      \n          Signed-off-by: Korov <korov9.c@gmail.com>\n      \n     + ## Documentation/git-checkout.adoc ##\n     +@@ Documentation/git-checkout.adoc: git checkout [-q] [-f] [-m] [<branch>]\n     + git checkout [-q] [-f] [-m] --detach [<branch>]\n     + git checkout [-q] [-f] [-m] [--detach] <commit>\n     + git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]\n     ++git checkout [-q] [-f] [-m] --create-if-missing <branch> [<start-point>]\n     + git checkout <tree-ish> [--] <pathspec>...\n     + git checkout <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]\n     + git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>...\n     +@@ Documentation/git-checkout.adoc: This will fail if there's an error checking out _<new-branch>_, for\n     + example if checking out the `<start-point>` commit would overwrite your\n     + uncommitted changes.\n     + \n     ++`git checkout --create-if-missing <branch> [<start-point>]`::\n     ++\n     ++\tCheck out _<branch>_ if it already exists, or create it from\n     ++\t_<start-point>_ before checking it out if it does not.\n     +++\n     ++When _<branch>_ does not already exist, this behaves like\n     ++`git checkout -b <branch> [<start-point>]`, including any `--track`\n     ++or `--no-track` options.\n     +++\n     ++When _<branch>_ already exists, the branch tip is not changed. If\n     ++`--track[=(direct|inherit)]` is given, the existing branch's upstream\n     ++configuration is updated using _<start-point>_ when one is provided,\n     ++or the current branch when _<start-point>_ is omitted. This form fails\n     ++when `HEAD` is detached and no _<start-point>_ is given.\n     ++\n     + `git checkout -B <branch> [<start-point>]`::\n     + \n     + \tThe same as `-b`, except that if the branch already exists it\n     +@@ Documentation/git-checkout.adoc: of it\").\n     + \tThe same as `-b`, except that if the branch already exists it\n     + \tresets _<branch>_ to the start point instead of failing.\n     + \n     ++`--create-if-missing <branch>`::\n     ++\tCheck out _<branch>_ if it already exists, or create it from\n     ++\t_<start-point>_ before checking it out if it does not.\n     +++\n     ++When _<branch>_ does not already exist, this behaves like\n     ++`git checkout -b <branch> [<start-point>]`, including any `--track`\n     ++or `--no-track` options.\n     +++\n     ++When _<branch>_ already exists, the branch tip is not changed. If\n     ++`--track[=(direct|inherit)]` is given, the existing branch's upstream\n     ++configuration is updated using _<start-point>_ when one is provided,\n     ++or the current branch when _<start-point>_ is omitted. This form fails\n     ++when `HEAD` is detached and no _<start-point>_ is given.\n     +++\n     ++This option cannot be used when checking out paths.\n     ++\n     + `-t`::\n     + `--track[=(direct|inherit)]`::\n     + \tWhen creating a new branch, set up \"upstream\" configuration. See\n     +\n       ## Documentation/git-switch.adoc ##\n      @@ Documentation/git-switch.adoc: SYNOPSIS\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>] -e <branch> [<start-point>]\n     ++git switch [<options>] --create-if-missing <branch> [<start-point>]\n       git switch [<options>] --orphan <new-branch>\n       \n       DESCRIPTION\n     @@ Documentation/git-switch.adoc: $ git branch -f _<new-branch>_\n       $ git switch _<new-branch>_\n       ------------\n       \n     -+`-e <branch>`::\n     -+`--ensure <branch>`::\n     ++`--create-if-missing <branch>`::\n      +\tSwitch to _<branch>_ if it already exists, or create it from\n      +\t_<start-point>_ before switching to it if it does not.\n      ++\n     @@ builtin/checkout.c: struct checkout_opts {\n       \tconst char *new_branch;\n       \tconst char *new_branch_force;\n       \tconst char *new_orphan_branch;\n     -+\tconst char *ensure_branch;\n     -+\tconst char *ensure_branch_start;\n     ++\tconst char *create_if_missing_branch;\n     ++\tconst char *create_if_missing_start;\n       \tint new_branch_log;\n       \tenum branch_track track;\n       \tstruct diff_options diff_options;\n     +@@ builtin/checkout.c: static int checkout_paths(const struct checkout_opts *opts,\n     + \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n     + \t\t    opts->new_branch);\n     + \n     ++\tif (opts->create_if_missing_branch)\n     ++\t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n     ++\t\t    opts->create_if_missing_branch);\n     ++\n     + \tif (!opts->checkout_worktree && !opts->checkout_index)\n     + \t\tdie(_(\"neither '%s' or '%s' is specified\"),\n     + \t\t    \"--staged\", \"--worktree\");\n      @@ builtin/checkout.c: static void update_refs_for_switch(const struct checkout_opts *opts,\n       \t\tfree(new_branch_info->refname);\n       \t\tnew_branch_info->name = xstrdup(opts->new_branch);\n       \t\tsetup_branch_path(new_branch_info);\n     -+\t} else if (opts->ensure_branch && opts->branch_exists &&\n     ++\t} else if (opts->create_if_missing_branch && opts->branch_exists &&\n      +\t\t   opts->track != BRANCH_TRACK_UNSPECIFIED) {\n     -+\t\tconst char *tracking_source = opts->ensure_branch_start ?\n     -+\t\t\topts->ensure_branch_start :\n     ++\t\tconst char *tracking_source = opts->create_if_missing_start ?\n     ++\t\t\topts->create_if_missing_start :\n      +\t\t\told_branch_info->name;\n     -+\t\tdwim_and_setup_tracking(the_repository, opts->ensure_branch,\n     ++\t\tdwim_and_setup_tracking(the_repository, opts->create_if_missing_branch,\n      +\t\t\t\t\ttracking_source, opts->track,\n      +\t\t\t\t\topts->quiet);\n     -+\t\tremote_state_clear(the_repository->remote_state);\n       \t}\n       \n       \told_desc = old_branch_info->name;\n     +@@ builtin/checkout.c: static void update_refs_for_switch(const struct checkout_opts *opts,\n     + \t\t\t\t\tfprintf(stderr, _(\"Switched to and reset branch '%s'\\n\"), new_branch_info->name);\n     + \t\t\t\telse\n     + \t\t\t\t\tfprintf(stderr, _(\"Switched to a new branch '%s'\\n\"), new_branch_info->name);\n     ++\t\t\t} else if (opts->create_if_missing_branch &&\n     ++\t\t\t\t   opts->branch_exists) {\n     ++\t\t\t\tfprintf(stderr, _(\"Switched to existing branch '%s'\\n\"),\n     ++\t\t\t\t\tnew_branch_info->name);\n     + \t\t\t} else {\n     + \t\t\t\tfprintf(stderr, _(\"Switched to branch '%s'\\n\"),\n     + \t\t\t\t\tnew_branch_info->name);\n      @@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const char *prefix,\n       \t\tdie(_(\"options '-%c', '-%c', and '%s' cannot be used together\"),\n       \t\t\tcb_option, toupper(cb_option), \"--orphan\");\n       \n     -+\tif (opts->ensure_branch) {\n     ++\tif (opts->create_if_missing_branch) {\n      +\t\tstruct strbuf ref = STRBUF_INIT;\n      +\t\tint exists;\n      +\n      +\t\tif (opts->new_branch || opts->new_branch_force || opts->new_orphan_branch)\n     -+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-e\", \"-c/-C/--orphan\");\n     ++\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--create-if-missing\", \"-c/-C/--orphan\");\n      +\t\tif (opts->force_detach)\n     -+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"-e\", \"--detach\");\n     ++\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--create-if-missing\", \"--detach\");\n      +\n     -+\t\texists = validate_branchname(opts->ensure_branch, &ref);\n     ++\t\texists = validate_branchname(opts->create_if_missing_branch, &ref);\n      +\t\tstrbuf_release(&ref);\n      +\n      +\t\t/* Save an explicit start point for tracking setup. */\n      +\t\tif (argc > 0 && opts->track != BRANCH_TRACK_UNSPECIFIED)\n     -+\t\t\topts->ensure_branch_start = argv[0];\n     ++\t\t\topts->create_if_missing_start = argv[0];\n      +\n      +\t\tif (exists) {\n      +\t\t\t/*\n     @@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const\n      +\t\t\topts->branch_exists = 1;\n      +\t\t} else {\n      +\t\t\t/* Branch doesn't exist: create it like -c */\n     -+\t\t\topts->new_branch = opts->ensure_branch;\n     ++\t\t\topts->new_branch = opts->create_if_missing_branch;\n      +\t\t}\n      +\t}\n      +\n     -+\tif (opts->ensure_branch && opts->branch_exists &&\n     ++\tif (opts->create_if_missing_branch && opts->branch_exists &&\n      +\t    opts->track != BRANCH_TRACK_UNSPECIFIED &&\n     -+\t    !opts->ensure_branch_start) {\n     ++\t    !opts->create_if_missing_start) {\n      +\t\tstruct object_id head_oid;\n      +\t\tchar *head = refs_resolve_refdup(get_main_ref_store(the_repository),\n      +\t\t\t\t\t\t \"HEAD\", 0, &head_oid, NULL);\n     @@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const\n       \n      -\t/* --track without -c/-C/-b/-B/--orphan should DWIM */\n      -\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n     -+\t/* --track without -c/-C/-b/-B/--orphan/-e should DWIM */\n     ++\t/* --track without -c/-C/-b/-B/--orphan/--create-if-missing should DWIM */\n      +\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch &&\n     -+\t    !(opts->ensure_branch && opts->branch_exists)) {\n     ++\t    !(opts->create_if_missing_branch && opts->branch_exists)) {\n       \t\tconst char *argv0 = argv[0];\n       \t\tif (!argc || !strcmp(argv0, \"--\"))\n       \t\t\tdie(_(\"--track needs a branch name\"));\n     @@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const\n       \t}\n       \n      +\t/*\n     -+\t * Handle -e with existing branch: set up new_branch_info to switch\n     -+\t * to the existing branch.\n     ++\t * Handle --create-if-missing with existing branch: set up\n     ++\t * new_branch_info to switch to the existing branch.\n      +\t */\n     -+\tif (opts->ensure_branch && opts->branch_exists) {\n     ++\tif (opts->create_if_missing_branch && opts->branch_exists) {\n      +\t\tstruct object_id rev;\n      +\n     ++\t\tif (repo_get_oid_mb(the_repository, opts->create_if_missing_branch,\n     ++\t\t\t\t    &rev))\n     ++\t\t\tdie(_(\"could not resolve '%s'\"),\n     ++\t\t\t    opts->create_if_missing_branch);\n     ++\n      +\t\tbranch_info_release(&new_branch_info);\n      +\t\tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n     -+\t\tnew_branch_info.name = xstrdup(opts->ensure_branch);\n     -+\t\tsetup_branch_path(&new_branch_info);\n     -+\n     -+\t\tif (new_branch_info.path &&\n     -+\t\t    !refs_read_ref(get_main_ref_store(the_repository),\n     -+\t\t\t\t   new_branch_info.path, &rev)) {\n     -+\t\t\tnew_branch_info.commit = lookup_commit_reference_gently(\n     -+\t\t\t\tthe_repository, &rev, 1);\n     -+\t\t\tif (new_branch_info.commit)\n     -+\t\t\t\tparse_commit_or_die(new_branch_info.commit);\n     -+\t\t}\n     ++\t\tsetup_new_branch_info_and_source_tree(&new_branch_info, opts, &rev,\n     ++\t\t\t\t\t\t      opts->create_if_missing_branch);\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     +@@ builtin/checkout.c: int cmd_checkout(int argc,\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_STRING_F(0, \"create-if-missing\", &opts.create_if_missing_branch, N_(\"branch\"),\n     ++\t\t\t     N_(\"create if needed and checkout branch\"),\n     ++\t\t\t     PARSE_OPT_NONEG),\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      @@ builtin/checkout.c: int cmd_switch(int argc,\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_STRING('e', \"ensure\", &opts.ensure_branch, N_(\"branch\"),\n     -+\t\t\t   N_(\"create if needed and switch to branch\")),\n     ++\t\tOPT_STRING_F(0, \"create-if-missing\", &opts.create_if_missing_branch, N_(\"branch\"),\n     ++\t\t\t     N_(\"create if needed and switch to branch\"),\n     ++\t\t\t     PARSE_OPT_NONEG),\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      \n     + ## contrib/completion/git-completion.bash ##\n     +@@ contrib/completion/git-completion.bash: _git_checkout ()\n     + \tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n     + \n     + \tcase \"$prev\" in\n     +-\t-b|-B|--orphan)\n     ++\t-b|-B|--orphan|--create-if-missing)\n     + \t\t# Complete local branches (and DWIM branch\n     + \t\t# remote branch names) for an option argument\n     + \t\t# specifying a new branch name. This is for\n     +@@ contrib/completion/git-completion.bash: _git_checkout ()\n     + \t\t;;\n     + \t*)\n     + \t\t# At this point, we've already handled special completion for\n     +-\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n     +-\t\t# things left we can possibly complete:\n     +-\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n     ++\t\t# the arguments to -b/-B, --orphan, and\n     ++\t\t# --create-if-missing. There are 3 main things left\n     ++\t\t# we can possibly complete:\n     ++\t\t# 1) a start-point for -b/-B, -d/--detach, --orphan,\n     ++\t\t#    or --create-if-missing\n     + \t\t# 2) a remote head, for --track\n     + \t\t# 3) an arbitrary reference, possibly including DWIM names\n     + \t\t#\n     + \n     +-\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n     ++\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan --create-if-missing\")\" ]; then\n     + \t\t\t__git_complete_refs --mode=\"refs\"\n     + \t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n     + \t\t\t__git_complete_refs --mode=\"remote-heads\"\n     +@@ contrib/completion/git-completion.bash: _git_switch ()\n     + \tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n     + \n     + \tcase \"$prev\" in\n     +-\t-c|-C|--orphan)\n     ++\t-c|-C|--orphan|--create-if-missing)\n     + \t\t# Complete local branches (and DWIM branch\n     + \t\t# remote branch names) for an option argument\n     + \t\t# specifying a new branch name. This is for\n     +@@ contrib/completion/git-completion.bash: _git_switch ()\n     + \t\tfi\n     + \n     + \t\t# At this point, we've already handled special completion for\n     +-\t\t# -c/-C, and --orphan. There are 3 main things left to\n     +-\t\t# complete:\n     +-\t\t# 1) a start-point for -c/-C or -d/--detach\n     ++\t\t# -c/-C, --orphan, and --create-if-missing. There\n     ++\t\t# are 3 main things left to complete:\n     ++\t\t# 1) a start-point for -c/-C, -d/--detach, or --create-if-missing\n     + \t\t# 2) a remote head, for --track\n     + \t\t# 3) a branch name, possibly including DWIM remote branches\n     + \n     +-\t\tif [ -n \"$(__git_find_on_cmdline \"-c -C -d --detach\")\" ]; then\n     ++\t\tif [ -n \"$(__git_find_on_cmdline \"-c -C -d --detach --create-if-missing\")\" ]; then\n     + \t\t\t__git_complete_refs --mode=\"refs\"\n     + \t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n     + \t\t\t__git_complete_refs --mode=\"remote-heads\"\n     +\n     + ## t/t2018-checkout-branch.sh ##\n     +@@ t/t2018-checkout-branch.sh: test_expect_success 'checkout -B to the current branch works' '\n     + \ttest_dirty_mergeable\n     + '\n     + \n     ++test_expect_success 'checkout --create-if-missing creates a branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout branch1 &&\n     ++\t\ttest_might_fail git branch -D create-if-missing-new\n     ++\t\" &&\n     ++\tgit checkout --create-if-missing create-if-missing-new $HEAD1 &&\n     ++\techo refs/heads/create-if-missing-new >expect &&\n     ++\tgit symbolic-ref HEAD >actual &&\n     ++\ttest_cmp expect actual &&\n     ++\ttest_cmp_rev $HEAD1 HEAD\n     ++'\n     ++\n     ++test_expect_success 'checkout --create-if-missing switches to existing branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout branch1 &&\n     ++\t\ttest_might_fail git branch -D create-if-missing-existing\n     ++\t\" &&\n     ++\tgit branch create-if-missing-existing $HEAD1 &&\n     ++\tgit checkout branch1 &&\n     ++\tgit checkout --create-if-missing create-if-missing-existing 2>err &&\n     ++\ttest_grep \"Switched to existing branch '\\''create-if-missing-existing'\\''\" err &&\n     ++\techo refs/heads/create-if-missing-existing >expect &&\n     ++\tgit symbolic-ref HEAD >actual &&\n     ++\ttest_cmp expect actual &&\n     ++\ttest_cmp_rev $HEAD1 HEAD\n     ++'\n     ++\n     + test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' '\n     + \tgit init src &&\n     + \ttest_commit -C src a &&\n     +@@ t/t2018-checkout-branch.sh: test_expect_success 'checkout -b rejects an extra path argument' '\n     + \ttest_grep \"Cannot update paths and switch to branch\" err\n     + '\n     + \n     ++test_expect_success 'checkout --create-if-missing rejects a path argument' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout branch1 &&\n     ++\t\ttest_might_fail git branch -D create-if-missing-path\n     ++\t\" &&\n     ++\tgit branch create-if-missing-path branch1 &&\n     ++\ttest_must_fail git checkout --create-if-missing create-if-missing-path -- file1 2>err &&\n     ++\ttest_grep \"Cannot update paths and switch to branch '\\''create-if-missing-path'\\''\" err\n     ++'\n     ++\n     + test_done\n     +\n     + ## t/t2027-checkout-track.sh ##\n     +@@ t/t2027-checkout-track.sh: test_expect_success 'checkout --track -b creates a new tracking branch' '\n     + \ttest $(git config --get branch.branch1.merge) = refs/heads/main\n     + '\n     + \n     ++test_expect_success 'checkout --create-if-missing --track creates branch from current branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout main &&\n     ++\t\tgit branch -D branch2\n     ++\t\" &&\n     ++\tgit checkout main &&\n     ++\tgit checkout --create-if-missing branch2 --track &&\n     ++\ttest $(git rev-parse --abbrev-ref HEAD) = branch2 &&\n     ++\ttest_cmp_config . branch.branch2.remote &&\n     ++\ttest_cmp_config refs/heads/main branch.branch2.merge\n     ++'\n     ++\n     ++test_expect_success 'checkout --create-if-missing --track uses current branch for existing branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout main &&\n     ++\t\tgit branch -D branch3 branch3-source\n     ++\t\" &&\n     ++\tgit checkout -b branch3-source main &&\n     ++\tgit branch branch3 main &&\n     ++\tgit checkout --create-if-missing branch3 --track >out 2>err &&\n     ++\ttest_grep \"branch '\\''branch3'\\'' set up to track '\\''branch3-source'\\''.\" out &&\n     ++\ttest_grep \"Switched to existing branch '\\''branch3'\\''\" err &&\n     ++\ttest_cmp_config . branch.branch3.remote &&\n     ++\ttest_cmp_config refs/heads/branch3-source branch.branch3.merge\n     ++'\n     ++\n     ++test_expect_success 'checkout --create-if-missing --track fails from detached HEAD without start-point' '\n     ++\ttest_when_finished \"\n     ++\t\tgit checkout main &&\n     ++\t\tgit branch -D branch4\n     ++\t\" &&\n     ++\tgit branch branch4 main &&\n     ++\tgit checkout --detach main &&\n     ++\ttest_must_fail git checkout --create-if-missing branch4 --track 2>err &&\n     ++\ttest_grep \"cannot set up tracking information; starting point '\\''HEAD'\\'' is not a branch\" err\n     ++'\n     ++\n     + test_expect_success 'checkout --track -b rejects an extra path argument' '\n     + \ttest_must_fail git checkout --track -b branch2 main one.t 2>err &&\n     + \ttest_grep \"cannot be used with updating paths\" err\n     +\n       ## t/t2060-switch.sh ##\n      @@ t/t2060-switch.sh: test_expect_success 'tracking info copied with autoSetupMerge=inherit' '\n       \ttest_cmp_config \"\" --default \"\" branch.main2.merge\n       '\n       \n     -+test_expect_success 'switch -e --track creates branch from current branch' '\n     ++test_expect_success 'switch --create-if-missing --track creates branch from current branch' '\n      +\ttest_when_finished \"\n      +\t\tgit switch main || :\n      +\t\tgit branch -D ensure-new-current || :\n      +\t\" &&\n      +\tgit switch main &&\n     -+\tgit switch -e ensure-new-current --track &&\n     ++\tgit switch --create-if-missing ensure-new-current --track &&\n      +\ttest_cmp_rev refs/heads/main refs/heads/ensure-new-current &&\n      +\ttest_cmp_config . branch.ensure-new-current.remote &&\n      +\ttest_cmp_config refs/heads/main branch.ensure-new-current.merge\n      +'\n      +\n     -+test_expect_success 'switch -e --track creates branch from remote-tracking branch' '\n     ++test_expect_success 'switch --create-if-missing --track creates branch from remote-tracking branch' '\n      +\ttest_when_finished \"\n      +\t\tgit switch main || :\n      +\t\tgit branch -D ensure-new || :\n      +\t\" &&\n     -+\tgit switch -e ensure-new --track origin/foo &&\n     ++\tgit switch --create-if-missing ensure-new --track origin/foo &&\n      +\ttest_cmp_rev refs/remotes/origin/foo refs/heads/ensure-new &&\n      +\ttest_cmp_config origin branch.ensure-new.remote &&\n      +\ttest_cmp_config refs/heads/foo branch.ensure-new.merge\n      +'\n      +\n     -+test_expect_success 'switch -e --track uses current branch for existing branch' '\n     ++test_expect_success 'switch --create-if-missing switches to existing branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit switch main || :\n     ++\t\tgit branch -D ensure-existing-plain || :\n     ++\t\" &&\n     ++\tgit branch ensure-existing-plain main &&\n     ++\tgit switch --create-if-missing ensure-existing-plain 2>err &&\n     ++\ttest_grep \"Switched to existing branch '\\''ensure-existing-plain'\\''\" err\n     ++'\n     ++\n     ++test_expect_success 'switch --create-if-missing reports tracking for existing branch' '\n     ++\ttest_when_finished \"\n     ++\t\tgit switch main || :\n     ++\t\tgit branch -D ensure-existing-report || :\n     ++\t\tgit update-ref refs/remotes/origin/foo first-branch || :\n     ++\t\" &&\n     ++\tgit branch ensure-existing-report first-branch &&\n     ++\tgit config branch.ensure-existing-report.remote origin &&\n     ++\tgit config branch.ensure-existing-report.merge refs/heads/foo &&\n     ++\tgit update-ref refs/remotes/origin/foo main &&\n     ++\tgit switch --create-if-missing ensure-existing-report >out 2>err &&\n     ++\ttest_grep \"Switched to existing branch '\\''ensure-existing-report'\\''\" err &&\n     ++\ttest_grep \"Your branch is behind '\\''origin/foo'\\''\" out\n     ++'\n     ++\n     ++test_expect_success 'switch --create-if-missing --track uses current branch for existing branch' '\n      +\ttest_when_finished \"\n      +\t\tgit switch main || :\n      +\t\tgit branch -D ensure-existing source-for-track || :\n      +\t\" &&\n      +\tgit switch -c source-for-track main &&\n      +\tgit branch ensure-existing main &&\n     -+\tgit switch -e ensure-existing --track &&\n     ++\tgit switch --create-if-missing ensure-existing --track >out 2>err &&\n     ++\ttest_grep \"branch '\\''ensure-existing'\\'' set up to track '\\''source-for-track'\\''.\" out &&\n     ++\ttest_grep \"Switched to existing branch '\\''ensure-existing'\\''\" err &&\n      +\ttest_cmp_config . branch.ensure-existing.remote &&\n      +\ttest_cmp_config refs/heads/source-for-track branch.ensure-existing.merge\n      +'\n      +\n     -+test_expect_success 'switch -e --track fails from detached HEAD without start-point' '\n     ++test_expect_success 'switch --create-if-missing --track fails from detached HEAD without start-point' '\n      +\ttest_when_finished \"\n      +\t\tgit switch main || :\n      +\t\tgit branch -D detached-target || :\n      +\t\" &&\n      +\tgit branch detached-target main &&\n      +\tgit switch --detach main &&\n     -+\ttest_must_fail git switch -e detached-target --track 2>stderr &&\n     ++\ttest_must_fail git switch --create-if-missing detached-target --track 2>stderr &&\n      +\ttest_grep \"cannot set up tracking information; starting point '\\''HEAD'\\'' is not a branch\" stderr\n      +'\n      +\n       test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '\n       \ttest_when_finished \"\n       \t\tgit worktree remove wt1 ||:\n     +\n     + ## t/t9902-completion.sh ##\n     +@@ t/t9902-completion.sh: test_expect_success 'double dash \"git checkout\"' '\n     + \t--quiet Z\n     + \t--detach Z\n     + \t--track Z\n     ++\t--create-if-missing=Z\n     + \t--orphan=Z\n     + \t--ours Z\n     + \t--theirs Z\n\n\n Documentation/git-checkout.adoc        | 32 +++++++++\n Documentation/git-switch.adoc          | 15 +++++\n builtin/checkout.c                     | 93 +++++++++++++++++++++++++-\n contrib/completion/git-completion.bash | 22 +++---\n t/t2018-checkout-branch.sh             | 37 ++++++++++\n t/t2027-checkout-track.sh              | 37 ++++++++++\n t/t2060-switch.sh                      | 73 ++++++++++++++++++++\n t/t9902-completion.sh                  |  1 +\n 8 files changed, 298 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc\nindex a8b3b8c2e2..a80f6fe6f6 100644\n--- a/Documentation/git-checkout.adoc\n+++ b/Documentation/git-checkout.adoc\n@@ -12,6 +12,7 @@ git checkout [-q] [-f] [-m] [<branch>]\n git checkout [-q] [-f] [-m] --detach [<branch>]\n git checkout [-q] [-f] [-m] [--detach] <commit>\n git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]\n+git checkout [-q] [-f] [-m] --create-if-missing <branch> [<start-point>]\n git checkout <tree-ish> [--] <pathspec>...\n git checkout <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]\n git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>...\n@@ -58,6 +59,21 @@ This will fail if there's an error checking out _<new-branch>_, for\n example if checking out the `<start-point>` commit would overwrite your\n uncommitted changes.\n \n+`git checkout --create-if-missing <branch> [<start-point>]`::\n+\n+\tCheck out _<branch>_ if it already exists, or create it from\n+\t_<start-point>_ before checking it out if it does not.\n++\n+When _<branch>_ does not already exist, this behaves like\n+`git checkout -b <branch> [<start-point>]`, including any `--track`\n+or `--no-track` options.\n++\n+When _<branch>_ already exists, the branch tip is not changed. If\n+`--track[=(direct|inherit)]` is given, the existing branch's upstream\n+configuration is updated using _<start-point>_ when one is provided,\n+or the current branch when _<start-point>_ is omitted. This form fails\n+when `HEAD` is detached and no _<start-point>_ is given.\n+\n `git checkout -B <branch> [<start-point>]`::\n \n \tThe same as `-b`, except that if the branch already exists it\n@@ -157,6 +173,22 @@ of it\").\n \tThe same as `-b`, except that if the branch already exists it\n \tresets _<branch>_ to the start point instead of failing.\n \n+`--create-if-missing <branch>`::\n+\tCheck out _<branch>_ if it already exists, or create it from\n+\t_<start-point>_ before checking it out if it does not.\n++\n+When _<branch>_ does not already exist, this behaves like\n+`git checkout -b <branch> [<start-point>]`, including any `--track`\n+or `--no-track` options.\n++\n+When _<branch>_ already exists, the branch tip is not changed. If\n+`--track[=(direct|inherit)]` is given, the existing branch's upstream\n+configuration is updated using _<start-point>_ when one is provided,\n+or the current branch when _<start-point>_ is omitted. This form fails\n+when `HEAD` is detached and no _<start-point>_ is given.\n++\n+This option cannot be used when checking out paths.\n+\n `-t`::\n `--track[=(direct|inherit)]`::\n \tWhen creating a new branch, set up \"upstream\" configuration. See\ndiff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc\nindex d6c4f229a5..461a6f0b96 100644\n--- a/Documentation/git-switch.adoc\n+++ b/Documentation/git-switch.adoc\n@@ -11,6 +11,7 @@ SYNOPSIS\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>] --create-if-missing <branch> [<start-point>]\n git switch [<options>] --orphan <new-branch>\n \n DESCRIPTION\n@@ -81,6 +82,20 @@ $ git branch -f _<new-branch>_\n $ git switch _<new-branch>_\n ------------\n \n+`--create-if-missing <branch>`::\n+\tSwitch to _<branch>_ if it already exists, or create it from\n+\t_<start-point>_ before switching to it if it does not.\n++\n+When _<branch>_ does not already exist, this behaves like\n+`git switch -c <branch> [<start-point>]`, including any `--track`\n+or `--no-track` options.\n++\n+When _<branch>_ already exists, the branch tip is not changed. If\n+`--track[=(direct|inherit)]` is given, the existing branch's upstream\n+configuration is updated using _<start-point>_ when one is provided,\n+or the current branch when _<start-point>_ is omitted. This form fails\n+when `HEAD` is detached and no _<start-point>_ is given.\n+\n `-d`::\n `--detach`::\n \tSwitch to a commit for inspection and discardable\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b78b3a1d16..f5bc882f2e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -81,6 +81,8 @@ struct checkout_opts {\n \tconst char *new_branch;\n \tconst char *new_branch_force;\n \tconst char *new_orphan_branch;\n+\tconst char *create_if_missing_branch;\n+\tconst char *create_if_missing_start;\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n@@ -551,6 +553,10 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n \t\t    opts->new_branch);\n \n+\tif (opts->create_if_missing_branch)\n+\t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n+\t\t    opts->create_if_missing_branch);\n+\n \tif (!opts->checkout_worktree && !opts->checkout_index)\n \t\tdie(_(\"neither '%s' or '%s' is specified\"),\n \t\t    \"--staged\", \"--worktree\");\n@@ -988,6 +994,14 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\tfree(new_branch_info->refname);\n \t\tnew_branch_info->name = xstrdup(opts->new_branch);\n \t\tsetup_branch_path(new_branch_info);\n+\t} else if (opts->create_if_missing_branch && opts->branch_exists &&\n+\t\t   opts->track != BRANCH_TRACK_UNSPECIFIED) {\n+\t\tconst char *tracking_source = opts->create_if_missing_start ?\n+\t\t\topts->create_if_missing_start :\n+\t\t\told_branch_info->name;\n+\t\tdwim_and_setup_tracking(the_repository, opts->create_if_missing_branch,\n+\t\t\t\t\ttracking_source, opts->track,\n+\t\t\t\t\topts->quiet);\n \t}\n \n \told_desc = old_branch_info->name;\n@@ -1030,6 +1044,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\t\t\t\tfprintf(stderr, _(\"Switched to and reset branch '%s'\\n\"), new_branch_info->name);\n \t\t\t\telse\n \t\t\t\t\tfprintf(stderr, _(\"Switched to a new branch '%s'\\n\"), new_branch_info->name);\n+\t\t\t} else if (opts->create_if_missing_branch &&\n+\t\t\t\t   opts->branch_exists) {\n+\t\t\t\tfprintf(stderr, _(\"Switched to existing branch '%s'\\n\"),\n+\t\t\t\t\tnew_branch_info->name);\n \t\t\t} else {\n \t\t\t\tfprintf(stderr, _(\"Switched to branch '%s'\\n\"),\n \t\t\t\t\tnew_branch_info->name);\n@@ -1927,6 +1945,52 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\tdie(_(\"options '-%c', '-%c', and '%s' cannot be used together\"),\n \t\t\tcb_option, toupper(cb_option), \"--orphan\");\n \n+\tif (opts->create_if_missing_branch) {\n+\t\tstruct strbuf ref = STRBUF_INIT;\n+\t\tint exists;\n+\n+\t\tif (opts->new_branch || opts->new_branch_force || opts->new_orphan_branch)\n+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--create-if-missing\", \"-c/-C/--orphan\");\n+\t\tif (opts->force_detach)\n+\t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--create-if-missing\", \"--detach\");\n+\n+\t\texists = validate_branchname(opts->create_if_missing_branch, &ref);\n+\t\tstrbuf_release(&ref);\n+\n+\t\t/* Save an explicit start point for tracking setup. */\n+\t\tif (argc > 0 && opts->track != BRANCH_TRACK_UNSPECIFIED)\n+\t\t\topts->create_if_missing_start = argv[0];\n+\n+\t\tif (exists) {\n+\t\t\t/*\n+\t\t\t * Branch exists: just switch to it, don't reset.\n+\t\t\t * We'll set up tracking after the switch if --track was given.\n+\t\t\t */\n+\t\t\topts->branch_exists = 1;\n+\t\t} else {\n+\t\t\t/* Branch doesn't exist: create it like -c */\n+\t\t\topts->new_branch = opts->create_if_missing_branch;\n+\t\t}\n+\t}\n+\n+\tif (opts->create_if_missing_branch && opts->branch_exists &&\n+\t    opts->track != BRANCH_TRACK_UNSPECIFIED &&\n+\t    !opts->create_if_missing_start) {\n+\t\tstruct object_id head_oid;\n+\t\tchar *head = refs_resolve_refdup(get_main_ref_store(the_repository),\n+\t\t\t\t\t\t \"HEAD\", 0, &head_oid, NULL);\n+\t\tconst char *branch;\n+\n+\t\tif (!head)\n+\t\t\tdie(_(\"failed to resolve HEAD as a valid ref\"));\n+\t\tif (!strcmp(head, \"HEAD\"))\n+\t\t\tdie(_(\"cannot set up tracking information; starting point '%s' is not a branch\"),\n+\t\t\t    \"HEAD\");\n+\t\tif (!skip_prefix(head, \"refs/heads/\", &branch))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t\tfree(head);\n+\t}\n+\n \tif (opts->overlay_mode == 1 && opts->patch_mode)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-p\", \"--overlay\");\n \n@@ -1961,8 +2025,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \tif (opts->new_orphan_branch)\n \t\topts->new_branch = opts->new_orphan_branch;\n \n-\t/* --track without -c/-C/-b/-B/--orphan should DWIM */\n-\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n+\t/* --track without -c/-C/-b/-B/--orphan/--create-if-missing should DWIM */\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch &&\n+\t    !(opts->create_if_missing_branch && opts->branch_exists)) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -2012,6 +2077,24 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\t/*\n+\t * Handle --create-if-missing with existing branch: set up\n+\t * new_branch_info to switch to the existing branch.\n+\t */\n+\tif (opts->create_if_missing_branch && opts->branch_exists) {\n+\t\tstruct object_id rev;\n+\n+\t\tif (repo_get_oid_mb(the_repository, opts->create_if_missing_branch,\n+\t\t\t\t    &rev))\n+\t\t\tdie(_(\"could not resolve '%s'\"),\n+\t\t\t    opts->create_if_missing_branch);\n+\n+\t\tbranch_info_release(&new_branch_info);\n+\t\tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n+\t\tsetup_new_branch_info_and_source_tree(&new_branch_info, opts, &rev,\n+\t\t\t\t\t\t      opts->create_if_missing_branch);\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@@ -2098,6 +2181,9 @@ int cmd_checkout(int argc,\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_STRING_F(0, \"create-if-missing\", &opts.create_if_missing_branch, N_(\"branch\"),\n+\t\t\t     N_(\"create if needed and checkout branch\"),\n+\t\t\t     PARSE_OPT_NONEG),\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@@ -2150,6 +2236,9 @@ int cmd_switch(int argc,\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_STRING_F(0, \"create-if-missing\", &opts.create_if_missing_branch, N_(\"branch\"),\n+\t\t\t     N_(\"create if needed and switch to branch\"),\n+\t\t\t     PARSE_OPT_NONEG),\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,\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex e875787710..1c72b4c853 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1740,7 +1740,7 @@ _git_checkout ()\n \tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n \n \tcase \"$prev\" in\n-\t-b|-B|--orphan)\n+\t-b|-B|--orphan|--create-if-missing)\n \t\t# Complete local branches (and DWIM branch\n \t\t# remote branch names) for an option argument\n \t\t# specifying a new branch name. This is for\n@@ -1762,14 +1762,16 @@ _git_checkout ()\n \t\t;;\n \t*)\n \t\t# At this point, we've already handled special completion for\n-\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n-\t\t# things left we can possibly complete:\n-\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n+\t\t# the arguments to -b/-B, --orphan, and\n+\t\t# --create-if-missing. There are 3 main things left\n+\t\t# we can possibly complete:\n+\t\t# 1) a start-point for -b/-B, -d/--detach, --orphan,\n+\t\t#    or --create-if-missing\n \t\t# 2) a remote head, for --track\n \t\t# 3) an arbitrary reference, possibly including DWIM names\n \t\t#\n \n-\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n+\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan --create-if-missing\")\" ]; then\n \t\t\t__git_complete_refs --mode=\"refs\"\n \t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n \t\t\t__git_complete_refs --mode=\"remote-heads\"\n@@ -2692,7 +2694,7 @@ _git_switch ()\n \tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n \n \tcase \"$prev\" in\n-\t-c|-C|--orphan)\n+\t-c|-C|--orphan|--create-if-missing)\n \t\t# Complete local branches (and DWIM branch\n \t\t# remote branch names) for an option argument\n \t\t# specifying a new branch name. This is for\n@@ -2721,13 +2723,13 @@ _git_switch ()\n \t\tfi\n \n \t\t# At this point, we've already handled special completion for\n-\t\t# -c/-C, and --orphan. There are 3 main things left to\n-\t\t# complete:\n-\t\t# 1) a start-point for -c/-C or -d/--detach\n+\t\t# -c/-C, --orphan, and --create-if-missing. There\n+\t\t# are 3 main things left to complete:\n+\t\t# 1) a start-point for -c/-C, -d/--detach, or --create-if-missing\n \t\t# 2) a remote head, for --track\n \t\t# 3) a branch name, possibly including DWIM remote branches\n \n-\t\tif [ -n \"$(__git_find_on_cmdline \"-c -C -d --detach\")\" ]; then\n+\t\tif [ -n \"$(__git_find_on_cmdline \"-c -C -d --detach --create-if-missing\")\" ]; then\n \t\t\t__git_complete_refs --mode=\"refs\"\n \t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n \t\t\t__git_complete_refs --mode=\"remote-heads\"\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex a48ebdbf4d..f910563170 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -243,6 +243,33 @@ test_expect_success 'checkout -B to the current branch works' '\n \ttest_dirty_mergeable\n '\n \n+test_expect_success 'checkout --create-if-missing creates a branch' '\n+\ttest_when_finished \"\n+\t\tgit checkout branch1 &&\n+\t\ttest_might_fail git branch -D create-if-missing-new\n+\t\" &&\n+\tgit checkout --create-if-missing create-if-missing-new $HEAD1 &&\n+\techo refs/heads/create-if-missing-new >expect &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n+\ttest_cmp_rev $HEAD1 HEAD\n+'\n+\n+test_expect_success 'checkout --create-if-missing switches to existing branch' '\n+\ttest_when_finished \"\n+\t\tgit checkout branch1 &&\n+\t\ttest_might_fail git branch -D create-if-missing-existing\n+\t\" &&\n+\tgit branch create-if-missing-existing $HEAD1 &&\n+\tgit checkout branch1 &&\n+\tgit checkout --create-if-missing create-if-missing-existing 2>err &&\n+\ttest_grep \"Switched to existing branch '\\''create-if-missing-existing'\\''\" err &&\n+\techo refs/heads/create-if-missing-existing >expect &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n+\ttest_cmp_rev $HEAD1 HEAD\n+'\n+\n test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' '\n \tgit init src &&\n \ttest_commit -C src a &&\n@@ -285,4 +312,14 @@ test_expect_success 'checkout -b rejects an extra path argument' '\n \ttest_grep \"Cannot update paths and switch to branch\" err\n '\n \n+test_expect_success 'checkout --create-if-missing rejects a path argument' '\n+\ttest_when_finished \"\n+\t\tgit checkout branch1 &&\n+\t\ttest_might_fail git branch -D create-if-missing-path\n+\t\" &&\n+\tgit branch create-if-missing-path branch1 &&\n+\ttest_must_fail git checkout --create-if-missing create-if-missing-path -- file1 2>err &&\n+\ttest_grep \"Cannot update paths and switch to branch '\\''create-if-missing-path'\\''\" err\n+'\n+\n test_done\ndiff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh\nindex c01f1cd617..67f073ddf0 100755\n--- a/t/t2027-checkout-track.sh\n+++ b/t/t2027-checkout-track.sh\n@@ -19,6 +19,43 @@ test_expect_success 'checkout --track -b creates a new tracking branch' '\n \ttest $(git config --get branch.branch1.merge) = refs/heads/main\n '\n \n+test_expect_success 'checkout --create-if-missing --track creates branch from current branch' '\n+\ttest_when_finished \"\n+\t\tgit checkout main &&\n+\t\tgit branch -D branch2\n+\t\" &&\n+\tgit checkout main &&\n+\tgit checkout --create-if-missing branch2 --track &&\n+\ttest $(git rev-parse --abbrev-ref HEAD) = branch2 &&\n+\ttest_cmp_config . branch.branch2.remote &&\n+\ttest_cmp_config refs/heads/main branch.branch2.merge\n+'\n+\n+test_expect_success 'checkout --create-if-missing --track uses current branch for existing branch' '\n+\ttest_when_finished \"\n+\t\tgit checkout main &&\n+\t\tgit branch -D branch3 branch3-source\n+\t\" &&\n+\tgit checkout -b branch3-source main &&\n+\tgit branch branch3 main &&\n+\tgit checkout --create-if-missing branch3 --track >out 2>err &&\n+\ttest_grep \"branch '\\''branch3'\\'' set up to track '\\''branch3-source'\\''.\" out &&\n+\ttest_grep \"Switched to existing branch '\\''branch3'\\''\" err &&\n+\ttest_cmp_config . branch.branch3.remote &&\n+\ttest_cmp_config refs/heads/branch3-source branch.branch3.merge\n+'\n+\n+test_expect_success 'checkout --create-if-missing --track fails from detached HEAD without start-point' '\n+\ttest_when_finished \"\n+\t\tgit checkout main &&\n+\t\tgit branch -D branch4\n+\t\" &&\n+\tgit branch branch4 main &&\n+\tgit checkout --detach main &&\n+\ttest_must_fail git checkout --create-if-missing branch4 --track 2>err &&\n+\ttest_grep \"cannot set up tracking information; starting point '\\''HEAD'\\'' is not a branch\" err\n+'\n+\n test_expect_success 'checkout --track -b rejects an extra path argument' '\n \ttest_must_fail git checkout --track -b branch2 main one.t 2>err &&\n \ttest_grep \"cannot be used with updating paths\" err\ndiff --git a/t/t2060-switch.sh b/t/t2060-switch.sh\nindex c91c4db936..f17afda28e 100755\n--- a/t/t2060-switch.sh\n+++ b/t/t2060-switch.sh\n@@ -146,6 +146,79 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '\n \ttest_cmp_config \"\" --default \"\" branch.main2.merge\n '\n \n+test_expect_success 'switch --create-if-missing --track creates branch from current branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-new-current || :\n+\t\" &&\n+\tgit switch main &&\n+\tgit switch --create-if-missing ensure-new-current --track &&\n+\ttest_cmp_rev refs/heads/main refs/heads/ensure-new-current &&\n+\ttest_cmp_config . branch.ensure-new-current.remote &&\n+\ttest_cmp_config refs/heads/main branch.ensure-new-current.merge\n+'\n+\n+test_expect_success 'switch --create-if-missing --track creates branch from remote-tracking branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-new || :\n+\t\" &&\n+\tgit switch --create-if-missing ensure-new --track origin/foo &&\n+\ttest_cmp_rev refs/remotes/origin/foo refs/heads/ensure-new &&\n+\ttest_cmp_config origin branch.ensure-new.remote &&\n+\ttest_cmp_config refs/heads/foo branch.ensure-new.merge\n+'\n+\n+test_expect_success 'switch --create-if-missing switches to existing branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-existing-plain || :\n+\t\" &&\n+\tgit branch ensure-existing-plain main &&\n+\tgit switch --create-if-missing ensure-existing-plain 2>err &&\n+\ttest_grep \"Switched to existing branch '\\''ensure-existing-plain'\\''\" err\n+'\n+\n+test_expect_success 'switch --create-if-missing reports tracking for existing branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-existing-report || :\n+\t\tgit update-ref refs/remotes/origin/foo first-branch || :\n+\t\" &&\n+\tgit branch ensure-existing-report first-branch &&\n+\tgit config branch.ensure-existing-report.remote origin &&\n+\tgit config branch.ensure-existing-report.merge refs/heads/foo &&\n+\tgit update-ref refs/remotes/origin/foo main &&\n+\tgit switch --create-if-missing ensure-existing-report >out 2>err &&\n+\ttest_grep \"Switched to existing branch '\\''ensure-existing-report'\\''\" err &&\n+\ttest_grep \"Your branch is behind '\\''origin/foo'\\''\" out\n+'\n+\n+test_expect_success 'switch --create-if-missing --track uses current branch for existing branch' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D ensure-existing source-for-track || :\n+\t\" &&\n+\tgit switch -c source-for-track main &&\n+\tgit branch ensure-existing main &&\n+\tgit switch --create-if-missing ensure-existing --track >out 2>err &&\n+\ttest_grep \"branch '\\''ensure-existing'\\'' set up to track '\\''source-for-track'\\''.\" out &&\n+\ttest_grep \"Switched to existing branch '\\''ensure-existing'\\''\" err &&\n+\ttest_cmp_config . branch.ensure-existing.remote &&\n+\ttest_cmp_config refs/heads/source-for-track branch.ensure-existing.merge\n+'\n+\n+test_expect_success 'switch --create-if-missing --track fails from detached HEAD without start-point' '\n+\ttest_when_finished \"\n+\t\tgit switch main || :\n+\t\tgit branch -D detached-target || :\n+\t\" &&\n+\tgit branch detached-target main &&\n+\tgit switch --detach main &&\n+\ttest_must_fail git switch --create-if-missing detached-target --track 2>stderr &&\n+\ttest_grep \"cannot set up tracking information; starting point '\\''HEAD'\\'' is not a branch\" stderr\n+'\n+\n test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '\n \ttest_when_finished \"\n \t\tgit worktree remove wt1 ||:\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 55dc9eabfc..e782c39c5e 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2588,6 +2588,7 @@ test_expect_success 'double dash \"git checkout\"' '\n \t--quiet Z\n \t--detach Z\n \t--track Z\n+\t--create-if-missing=Z\n \t--orphan=Z\n \t--ours Z\n \t--theirs Z\n\nbase-commit: 0fae78c9d55efe705877ea537fe42c59164ccd94\n-- \ngitgitgadget\n"}]}