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

8

898

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.

Ikeda answered 30/7, 2019 at 6:46 Comment(9)
There's a good article at InfoQ about this topic: infoq.com/news/2019/08/git-2-23-switch-restoreVinegarette
Is the Git team planning to deprecate git checkout? I see no deprecation warnings when using it. But with git switch and git restore, I now see no need for checkout. However, if it is not deprecated, the Git team has just made things more confusing, not less. Does anyone know the plans for git checkout? If it does still have a use case, can someone add or edit an answer to elaborate its use case.Gluten
@MikeWilliamson I don't think so, you still need git checkout <commit>.Ikeda
@BastianVenthur That's right, thanks! Unfortunately, that means checkout will stick around just for its "corner case" that is used the minority of the time. This will stretch out the time of confusion. But I guess we'll just have to help each other out through it. :)Gluten
@BastianVenthur you can git switch --detach <commit>Optic
@MikeWilliamson — I addressed that in my answer (https://mcmap.net/q/11974/-what-39-s-the-difference-between-39-git-switch-39-and-39-git-checkout-39-lt-branch-gt). Namely, the commit that added switch explains that git checkout isn't going anywhere: "The good old 'git checkout' command is still here and will be until all (or most of users) are sick of it." That also means that there is no need for checkout not covered by switch and restore, beyond ensuring existing user scripts and the like continue to function.Manumit
The idea of a more intuitive word and a separated functionality according to that is a good idea, but as of this date, git switch still has a legend git-switch THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE, so I will advice to use it manually but not yet in automated Git OperationsAnt
Related: How do I check out a remote Git branch?Raspings
From the answers it is not clear if switch branch behaves EXACTLY equals than checkout branchMellon
M
271

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.
Manumit answered 22/12, 2021 at 20:32 Comment(0)
K
776

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.

Kkt answered 30/7, 2019 at 7:0 Comment(15)
This seems like a good change. Make a branch? git checkout Switch branch? git checkout Get a certain version of a file? git checkout Remove changes to one file? git checkout Honestly I'm wondering how much of the normal git workflow could be done with various flags to git checkout.Overstreet
So is the idea now that git checkout isn't technically needed for anything anymore? Or is it still in use for certain things, such as checking out a commit that isn't a branch head (moving to "detached head" mode)?Desirable
@CaptainMan, making a new branch is done with git branch. Which however only creates the branch and does not switch to it (another peculiar design decision). If you want to go on working in the new branch, you need to checkout into it. There is the option checkout -b <branch name> which is a shortcut for git branch + git checkout. Still, the checkout operation does not technically create a new branch.Sonata
@Sonata How can you say checkout doesn't make branches one sentence after you say checkout makes branches? It doesn't matter the internal workings of the -b flag. It still makes a branch.Overstreet
@CaptainMan, the checkout operation does not create a branch, it can only switch into an already existing branch. The -b option to the checkout command internally performs a git branch before it actually does the checkout. This is the same like git pull is a shortcut for git fetch + git merge.Sonata
@Sonata I understand your point about operations versus commands, but this discussion is about commands. Unless we're using super low level APIs we don't have direct access to the "operations". We only have access to the commands. It's a needlessly pedantic distinction here.Overstreet
@Sonata So in your git pull example, would you also say that the "pull operation" does not fetch objects from the remote?Liaotung
@forresthopkinsa, there is no such thing as a "pull operation". git pull is exactly a shortcut for git fetch followed by git merge (or git rebase, if you pass the --rebase option or if your default integration strategy is rebasing instead of merging). There is NOTHING more that pull does other than calling those two operations. Look at the docs and you'll see that ALL of the options for pull are options of fetch or merge. Actually, I have not used git pull for years. I always do a git fetch first, then look at the history of what has changed, and then do a git merge.Sonata
You're so close to grasping my actual point: if I say pull, I mean fetch+merge; if I say checkout, I include branch creation in its list of capabilities.Liaotung
Helpful note: for those used to git checkout -b <branch name> you can use git switch -c <branch name> to get the same effectFlorencia
There are so many commands and flags in Git, and they added even more ways of doing the same thing..Himmler
@Sonata I totally agree with your points, but since so many people don't get them the git developers created the new command switch and restore to accommodate the masses ;) And to be honest that is a good thing, even if that is pointless or even annoying for advance git usersVeradi
I totally get why this has been introduced, git-checkout was a bit bloated & semantically broken. But really would help if man git-switch would be more explicit about why they have introduced this i.e. to break up ` git-checkout` functionality.Nicaragua
What git-checkout does is actually checking out a hash object: commit (commit object), tag (ref to a commit object), branch (ref to a commit object too), file (blob object), etc. It is confusing indeed, unless one knows some internals of Git.Washer
I am personally of the opinion that this is the dumbing down of Git. :( But if it helps people so be it...Programme
M
271

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.
Manumit answered 22/12, 2021 at 20:32 Comment(0)
M
254

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.

Magner answered 30/7, 2019 at 7:0 Comment(5)
As you mentioned having a branch and file with the same name is confusing. I assume the branch takes priority over the file, as that would be often more desirable? Or how does that work?Ubangi
@Ubangi Yes, that is correct. If a branch and a file have the same name then doing git checkout <name> gives preference to the branch instead of the file.Serried
If you want to checkout the file instead of the branch you have to separate it with -- from the options. This is a common idiom for many Git and other Unix commands.Barometrograph
@Barometrograph and SteveTurczyn, git checkout <filename> frequently does work, but git checkout -- <filename> is better, as the -- clearly indicates to the git parser that the options being passed to git checkout have ended, and a list of file or directory paths has begun. This may be important, for instance, if your filename begins with a dash, such as -myfile. Doing git checkout -- -myfile should work in that case, whereas without the preceding --, the -myfile would look like a messed-up option passed to git checkout.Fully
@Barometrograph You said instead and common. No, the Unix -- idiom does not separate a git file from a git branch. This idiom stops parser to parse the - prefixed options and keep parser to parse positional arguments. There is nothing to do here with the git idioms.Bustos
O
7

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)

Overseer answered 21/6, 2020 at 20:0 Comment(2)
I'd argue that this is acutally a feature and not a bug (limitation). switch is created for the single purpose of changing branches, and when you do that, you do want to be at the HEAD of that branch. checkout is a more general operation which brings your working copy in line with any given state in the history (= commit). Since any branch name is an alias for the HEAD commit of that branch, checking out a branch is technically no different than checking out any other commit.Sonata
with -d you can: git switch -d 6c13Suppliant
N
2

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

Normalize answered 15/9, 2022 at 18:14 Comment(4)
That's a good findManumit
The fact that there is no --force option in restore hints to me that it was probably there for the branch switch scenario, not the file restoration one.Manumit
@TTT, done. To the point of your answer, does this mean that checkout -f is equivalent to git reset --hard ; git switch <branch> (without -f in the switch)? Or do I overlook some side effects of this?Sonata
@Sonata yes, that should have the same effect. (I don't know for sure that it's exactly the same but I can't think of a way it might differ.) That's a nice restatement of the answer as well: switch -f doesn't attempt the reset like checkout -f does.Normalize
I
1

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.

Ignescent answered 30/7, 2023 at 13:32 Comment(0)
D
0

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.

Dessiedessma answered 17/5, 2023 at 21:8 Comment(0)
W
-4

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

Wiseman answered 8/12, 2022 at 3:51 Comment(1)
The -b flag creates a new branch. git checkout [branch] will only give you a detached HEAD if the target isn't a branch name. If it is a branch name, it'll check it out, the same as git switch.Manumit

© 2022 - 2024 — McMap. All rights reserved.