What's the difference between 'git switch' and 'git checkout' <branch>?

319.6k Views Asked by At

Git 2.23 introduces a new command git switch. After reading the documentation, it seems pretty much the same as git checkout <branchname>. What is the difference or use case?

Two new commands "git switch" and "git restore" are introduced to split "checking out a branch to work on advancing its history" and "checking out paths out of the index and/or a tree-ish to work on advancing the current history" out of the single "git checkout" command.

8

There are 8 best solutions below

0
On BEST ANSWER

The switch command indeed does the same thing as checkout, but only for those usages that switch branches. It cannot restore working tree files — that is done using restore, the other command split off from checkout.

The split checkout commands, in summary:

  • switch — Switch to a specified branch
  • restore — Restore file(s) from another branch or source

Detailed explanation

As you noted in the 2.23.0 release notes section you quoted, the switch and restore commands were introduced to split the checkout command into two separate pieces:

  • "checking out a branch to work on advancing its history"
  • "checking out paths out of the index and/or a tree-ish to work on advancing the current history"

In other words, checkout does two different things, and this release split each of those different things into its own focused command.

This dual purpose of checkout can be seen in its summary description in the documentation:

git-checkout - Switch branches or restore working tree files

The commit that added the switch command explains the rationale for the new commands in its commit message:

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

From this, it's clear that the new commands were introduced to reduce confusion by having two focused commands, rather than one multi-purpose command.

Note that (as of March 2024) the new commands are still listed as experimental (switch, restore):

THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.

Command comparison

I have not found a full comparison of the commands anywhere. From reading through the documentation, I think this should be a fairly complete comparison:

previous command new command
git checkout <branch> git switch <branch>
git checkout N/A (use git status)
git checkout -b <new_branch> [<start_point>] git switch -c <new-branch> [<start-point>]
git checkout -B <new_branch> [<start_point>] git switch -C <new-branch> [<start-point>]
git checkout --orphan <new_branch> git switch --orphan <new-branch>
git checkout --orphan <new_branch> <start_point> N/A (use git switch <start-point> then git switch --orphan <new-branch>)
git checkout [--detach] <commit> git switch --detach <commit>
git checkout --detach [<branch>] git switch --detach [<branch>]
git checkout [--] <pathspec>… git restore [--] <pathspec>…
git checkout --pathspec-from-file=<file> git restore --pathspec-from-file=<file>
git checkout <tree-ish> [--] <pathspec>… git restore -s <tree> [--] <pathspec>…
git checkout <tree-ish> --pathspec-from-file=<file> git restore -s <tree> --pathspec-from-file=<file>
git checkout -p [<tree-ish>] [--] [<pathspec>…] git restore -p [-s <tree>] [--] [<pathspec>…]

As shown by this comparison, some prior usages can be converted to the new commands by simply replacing the old command name (checkout) with the new one (switch, restore), whereas others require additional adjustment. Notable changes include:

  • The -b/-B options for creating a new branch before switching are renamed to -c/-C. They also have long option variants (--create/--force-create), unlike before.
  • --detach (or -d) is now always required when switching to a detached head, where it was previously optional for commits but required for branches.
  • The source tree for restoring is now given by the -s (or --source) option, rather than being an inline argument.
  • Switching using --force (or -f) now fails if there are unmerged entries, rather than ignoring them. --force has also been renamed to --discard-changes, with --force being kept as an alias.
1
On

git switch ~= git checkout -b [branch]. git checkout [branch] will give you a detached HEAD.

0
On

The answer by M. Justin was really great. However, I just noticed that there is a difference in the behavior between git checkout --orphan and git switch --orphan:

git checkout --orphan new-branch
git status

Output:

On branch new-branch

No commits yet

Changes to be committed:
.....

will checkout to the new-branch and will keep the tracked files.

While using switch will erase all the tracked files with a literally fresh branch:

git switch --orphan new-branch
git status

Output:

Switched to a new branch 'new-branch'

My Git version is 2.34.1.

0
On

Apart from the similarities and differences explained by other users, the Git command switch can be safer than checkout if your intention is "just change the branch."

With checkout, you can accidentally "restore" your work-in-progress files to their original state (and they are gone) like in this case. A single mistake like typing checkout instead of add can lead to a loss of changes (and I did this mistake by frequently changing branches and creating commits).

Therefore, using the switch command can prevent this kind of loss of changes.

4
On

tl;dr: When using checkout with --force, you can switch branches while in the middle of a merge. You can't do this with switch.

Details:

The other answers have already covered the motivation behind splitting checkout into switch and restore, as well as the fact that syntactic usage differences exist. (For example you can use checkout with a commit or a remote tracking branch such as origin/main directly, but with switch you must also explicitly specify the --detach option.) However, I have found at least one significant functionality difference too.

git switch -f is documented as such:

Proceed even if the index or the working tree differs from HEAD. Both the index and working tree are restored to match the switching target. If --recurse-submodules is specified, submodule content is also restored to match the switching target. This is used to throw away local changes.

And similarly, git checkout -f is documented as such (emphasis on the last sentence):

When switching branches, proceed even if the index or the working tree differs from HEAD, and even if there are untracked files in the way. This is used to throw away local changes and any untracked files or directories that are in the way.

When checking out paths from the index, do not fail upon unmerged entries; instead, unmerged entries are ignored.

It seems like the last sentence applies to the other meaning of checkout, i.e. the restore command equivalent. However, when attempting to switch branches while you are currently sitting in the middle of a merge, git checkout -f will succeed, even if you have unresolved conflicts at that moment. git switch -f does not work at all if you are currently merging (even without conflicts), as you get this error message:

fatal: cannot switch branch while merging

Note: this difference was tested using Git version 2.37.1

2
On

switch has some limitations: at the moment you can switch from any commit to <branch name>. However, it's impossible to switch from <branch name> to a particular commit with a status of detached HEAD.

So you need to use git checkout 5efb (where 5efb is an example of a hash reference to an arbitrary commit)

5
On

git checkout is a bit of a Swiss Army knife in that it has several unrelated uses.

If you modify a file, but haven't staged the change, then git checkout <filename> will reverse the modifications... a quick and easy way to cancel changes to a file. You remain in the same branch.

git checkout <branchname> (as you noted) switches branches.

It is two completely different purposes, which could lead to confusion if a file name and a branch name are similar.

Having it as two commands is clearer.

15
On

Well, according to the documentation you link to, its sole purpose is to split and clarify the two different uses of git checkout:

  • git switch can now be used to change branches, as git checkout <branchname> does
  • git restore can be used to reset files to certain revisions, as git checkout -- <path_to_file> does

People are confused by these different ways to use git checkout, as you can see from the many questions regarding git checkout here on Stack Overflow. Git developers seem to have taken this into account.