Git

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:

Bash
git checkout main        # harmless: switch branch
git checkout config.py   # silently throws away your uncommitted edits

Same 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. 🤩

OldNewJob
git checkout maingit switch mainswitch branch
git checkout -b featgit switch -c featcreate and switch
git checkout -B featgit switch -C featcreate or reset, then switch
git checkout -git switch -back to previous branch
git checkout abc123git switch --detach abc123inspect a commit
git checkout -- file.pygit restore file.pydiscard working-tree changes
git checkout abc123 -- file.pygit restore --source=abc123 file.pypull 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.

Bash
git switch v1.4.2

refuses:

YAML
fatal: a branch is expected, got tag

You 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

Leave a Reply

Your email address will not be published. Required fields are marked *