git switch and git restore: the split you probably missed (as I did)
git checkout has been split into git switch and git restore.
git checkout main and git switch main do exactly the same thing. So why does the second one exist?
Because checkout is two commands wearing one name. It moves HEAD to another branch, and it overwrites files in your working tree from some commit. Those operations have nothing in common except that both touch files on disk — and one of them is destructive.
That collision produces a genuinely bad failure mode:
git checkout main # harmless: switch branch
git checkout config.py # silently throws away your uncommitted editsSame verb, same shape, no confirmation, no reflog entry to crawl back through. The working-tree copy is simply gone. And when a branch and a path share a name, you get to remember which side of a -- separator each argument belongs on.
The split
Git 2.23 (August 2019) carved checkout into two purpose-built commands: switch for refs, restore for file contents. For six years they carried a THIS COMMAND IS EXPERIMENTAL banner in the man pages, which is why plenty of people tried them once and went back. That banner is gone: Git 2.51 (August 2025) declared both stable, with a command-line interface guaranteed to stay backwards compatible.
If you last looked in 2019 and filed them under “wait and see” — the waiting is over. 🤩
| Old | New | Job |
|---|---|---|
git checkout main | git switch main | switch branch |
git checkout -b feat | git switch -c feat | create and switch |
git checkout -B feat | git switch -C feat | create or reset, then switch |
git checkout - | git switch - | back to previous branch |
git checkout abc123 | git switch --detach abc123 | inspect a commit |
git checkout -- file.py | git restore file.py | discard working-tree changes |
git checkout abc123 -- file.py | git restore --source=abc123 file.py | pull a file out of a commit |
What you actually gain
Detached HEAD becomes deliberate. git checkout v1.4.2 drops you into detached HEAD with a paragraph of explanation you’ve long since learned to scroll past.
git switch v1.4.2refuses:
fatal: a branch is expected, got tagYou have to write --detach to say you meant it. That single change removes the most common way beginners lose commits — and the most common way the rest of us end up committing onto nothing at 2 a.m.
Destructive actions are spelled out. switch‘s forcing flag is --discard-changes. The -f alias still works, but the long form says what it costs, and that’s the one that ends up in your shell history and your team’s runbook.
switch cannot touch files. It takes no pathspec at all. There is no argument you can typo into a file-clobbering command.
restore separates the two places a change lives. --worktree (the default), --staged, or both. checkout conflated the index and the working tree; reset was the other half of the answer. Now one command covers restoring content, and its scope is explicit.
No, checkout isn’t going anywhere
The Git project has been clear that checkout stays. Every tutorial, script, CI pipeline, and muscle memory on earth depends on it, and nothing is served by breaking that.
But muscle memory is exactly the argument for switching. checkout reads the same whether it’s about to change your branch or delete your afternoon’s work. switch and restore tell you which one you asked for, before you press Enter.
Sources: git-switch documentation, Highlights from Git 2.51, Highlights from Git 2.23