782

Git 2.23 introduces a new command git switch -- after reading the docs, it seems pretty much the same as git checkout <branchname> can someone explain 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.

Bastian Venthur
  • 12,515
  • 5
  • 44
  • 78
  • 16
    There's a good article at InfoQ about this topic: https://www.infoq.com/news/2019/08/git-2-23-switch-restore/ – rsenna Sep 09 '19 at 11:07
  • 12
    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. – Mike Williamson Feb 11 '21 at 13:27
  • 4
    @MikeWilliamson I don't think so, you still need `git checkout `. – Bastian Venthur Feb 12 '21 at 10:47
  • 4
    @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. :) – Mike Williamson Feb 15 '21 at 07:55
  • 7
    @BastianVenthur you can `git switch --detach ` – benftwc Sep 02 '21 at 13:11
  • 1
    @MikeWilliamson — I addressed that in my answer (https://stackoverflow.com/a/70454786/1108305). Namely, the [commit](https://github.com/git/git/commit/d787d311dbd7a4104a9dde23b90ae24528a15cf9) 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. – M. Justin Feb 03 '22 at 16:33
  • 1
    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](https://git-scm.com/docs/git-switch) _THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE_, so I will advice to use it manually but not yet in automated Git Operations – IF.Francisco.ME Aug 31 '23 at 12:40

8 Answers8

709

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 Stackoverflow. Git developers seem to have taken this into account.

IMSoP
  • 89,526
  • 13
  • 117
  • 169
kowsky
  • 12,647
  • 2
  • 28
  • 41
  • 221
    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`. – Captain Man Feb 13 '20 at 16:36
  • 26
    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)? – PieterNuyts Apr 17 '20 at 12:57
  • 10
    @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 ` which is a shortcut for `git branch` + `git checkout`. Still, the `checkout` operation does not technically create a new branch. – Mike Jun 09 '20 at 23:24
  • 22
    @Mike 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. – Captain Man Jun 10 '20 at 02:07
  • 13
    @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`. – Mike Jun 11 '20 at 05:51
  • 25
    @Mike 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. – Captain Man Jun 11 '20 at 14:57
  • 1
    @Mike So in your `git pull` example, would you also say that the "pull operation" does *not* fetch objects from the remote? – forresthopkinsa Aug 12 '20 at 17:38
  • 5
    @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`. – Mike Aug 13 '20 at 19:10
  • 7
    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. – forresthopkinsa Aug 17 '20 at 02:36
  • 74
    Helpful note: for those used to `git checkout -b ` you can use `git switch -c ` to get the same effect – Default Aug 23 '20 at 15:38
  • 15
    There are so many commands and flags in Git, and they added even more ways of doing the same thing.. – Stanislav Bashkyrtsev Nov 23 '20 at 15:35
  • 2
    @Mike 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 users – luca Feb 15 '22 at 13:31
  • 1
    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. – Amos Folarin May 11 '22 at 09:26
  • 3
    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. – Leo Jul 28 '22 at 16:45
  • 1
    I am personally of the opinion that this is the dumbing down of Git. :( But if it helps people so be it... – Dheeraj Pb Oct 27 '22 at 05:41
232

git checkout is a bit of a swiss army knife in that 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.

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.

SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • 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? – AgentM Apr 29 '20 at 17:29
  • @AgentM Yes, that is correct. If a branch and a file have the same name then doing `git checkout ` gives preference to the branch instead of the file. – Kartik Soneji Sep 02 '20 at 17:26
  • 8
    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. – bjhend Jan 30 '21 at 15:26
  • 5
    @bjhend and SteveTurczyn, `git checkout ` frequently does work, but `git checkout -- ` 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`. – Gabriel Staples Mar 15 '22 at 22:35
206

The switch command indeed does the same thing as checkout, but only for those usages that switch branches. In particular, unlike checkout it cannot restore working tree files — that is instead done using the restore command that was introduced alongside switch.

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 December 2021, 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 in this comparison, some prior usages can be converted to the new commands by replacing the old command name (checkout) to 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), whereas previously they only had the single letter option versions.
  • --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 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.
M. Justin
  • 14,487
  • 7
  • 91
  • 130
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 arbitrary commit)

daGo
  • 2,584
  • 26
  • 24
  • 21
    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. – Mike Jun 26 '20 at 17:00
  • 69
    with `-d` you can: `git switch -d 6c13` – Mendi Barel Aug 01 '20 at 21:23
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

TTT
  • 22,611
  • 8
  • 63
  • 69
  • 1
    That's a good find – M. Justin Nov 29 '22 at 16:25
  • 1
    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. – M. Justin Nov 29 '22 at 17:04
  • 1
    @TTT, done. To the point of your answer, does this mean that `checkout -f` is equivalent to `git reset --hard ; git switch ` (without `-f` in the `switch`)? Or do I overlook some side effects of this? – Mike Dec 01 '22 at 05:06
  • 1
    @Mike 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. – TTT Dec 01 '22 at 15:37
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 it's 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 switch command can prevent this kind of loss of changes.

tada-s
  • 3
  • 2
0

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

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
Switched to a new branch 'new-branch'

my git version is 2.34.1

hassan
  • 7,812
  • 2
  • 25
  • 36
-4

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

Adam
  • 5
  • 1
  • 2
    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`. – M. Justin Dec 08 '22 at 15:43