Background: Why Does git checkout Have a Problem?
If you’ve used Git long enough, you’ve almost certainly typed git checkout in very different situations without realizing how logically distinct they actually are:
# Situation 1: Switch to another branch
git checkout main
# Situation 2: Create a new branch and switch to it
git checkout -b feature/login
# Situation 3: Discard unstaged changes in a file
git checkout -- src/auth.js
# Situation 4: Checkout to a specific commit (detach HEAD)
git checkout a1b2c3d
One command, four completely different behaviors. Situations 3 and 4 look nearly identical syntactically but produce completely opposite results. I’ve seen junior team members accidentally type git checkout HEAD~1 instead of git checkout -- . and end up in detached HEAD state without realizing what happened.
Git 2.23 (August 2019) officially introduced git switch and git restore to clearly separate these responsibilities:
git switch— does exactly one thing: switch branchesgit restore— does exactly one thing: restore files to a previous state
Each command does exactly one thing. When a team includes both newcomers and seasoned Git veterans, that clarity saves a significant amount of explanation time.
Setup: Check Your Git Version First
These two commands require at least Git 2.23 — released in August 2019. Check first:
git --version
# git version 2.43.0
If your version is lower than 2.23, you’ll need to update:
# Ubuntu/Debian
sudo add-apt-repository ppa:git-core/ppa
sudo apt update && sudo apt install git
# macOS (Homebrew)
brew upgrade git
# Arch Linux
sudo pacman -S git
# Windows (winget)
winget upgrade --id Git.Git
After updating, verify again:
git --version
# Must be >= 2.23
Detailed Guide: How to Use git switch and git restore
1. git switch — Safe Branch Switching
Essentially, git switch does exactly what the branch-switching part of git checkout does — nothing more:
# Switch to an existing branch
git switch main
git switch develop
# Create a new branch and switch to it (replaces checkout -b)
git switch -c feature/user-auth
# Create a branch from a specific point
git switch -c hotfix/payment-bug origin/main
# Switch back to the previous branch (like cd -)
git switch -
A major advantage: if you type a branch name that doesn’t exist, git switch gives a clear error instead of doing something unexpected:
git switch non-existent-branch
# fatal: invalid reference: non-existent-branch
As for detaching HEAD — you now have to explicitly use the --detach flag, so it can’t happen accidentally anymore:
git switch --detach a1b2c3d
# HEAD is now at a1b2c3d...
2. git restore — File Recovery with Clear Control
In day-to-day practice, git restore is the command I reach for more often — accidentally staging files happens more frequently than you might think.
Discard unstaged changes (working directory):
# Discard changes in a specific file
git restore src/auth.js
# Discard all unstaged changes
git restore .
Unstage accidentally added files:
# Unstage a single file
git restore --staged src/config.js
# Unstage everything
git restore --staged .
Important note: --staged only removes the file from the staging area. The changes remain in the file — they’re just no longer marked for commit.
Restore a file to a specific commit or branch:
# Get a file from another branch (no need to checkout that branch)
git restore --source=feature/new-api src/api.js
# Restore a file to its state 3 commits ago
git restore --source=HEAD~3 src/database.js
# Get a file from a specific commit
git restore --source=a1b2c3d src/utils.js
Use --source when you need a specific version of a file from another branch or commit — no need to check out there, and it doesn’t affect your current branch at all.
3. Combining --staged and --worktree
By default, git restore only affects the working tree. To reset both staged and working tree to HEAD at the same time:
# Restore both staged and working tree to HEAD
git restore --staged --worktree src/auth.js
# Equivalent to: git checkout HEAD -- src/auth.js (old style)
4. Quick Comparison: Old Commands vs New Commands
# === Switch branch ===
git checkout main # Old
git switch main # New
# === Create new branch ===
git checkout -b feature/x # Old
git switch -c feature/x # New
# === Discard unstaged changes ===
git checkout -- src/auth.js # Old
git restore src/auth.js # New
# === Unstage file ===
git reset HEAD src/config.js # Old
git restore --staged src/config.js # New
# === Get file from another commit ===
git checkout a1b2c3d -- src/utils.js # Old
git restore --source=a1b2c3d src/utils.js # New
Adopting This in Your Team: A Smooth Transition
Set Up Aliases for a Gradual Transition
When I rolled this out to a team of 8, nobody wanted to change their habits overnight. My approach: set up aliases so both styles work — but internal docs only reference the new commands:
# Add to ~/.gitconfig
[alias]
sw = switch
swc = switch -c
rs = restore
rss = restore --staged
PR templates and commit guides use only the new commands. No training sessions or announcements needed — about two months later, the whole team had switched on their own without any prompting.
Verify After Restoring
After restoring, check immediately with git status:
# After discarding changes, verify
git restore src/auth.js
git status
# src/auth.js no longer appears under "Changes not staged for commit"
# After unstaging, verify
git restore --staged src/config.js
git status
# src/config.js moves from "Changes to be committed" to "Changes not staged"
# View the actual diff
git diff HEAD src/auth.js
Common Points of Confusion
A few things people commonly ask when switching over:
- You can’t switch with uncommitted changes — Git throws an error if changes might be overwritten. Use
git stashfirst, orgit switch --discard-changesto throw them away (but be careful with this one). git restorecan’t remove untracked files — for newly created files that have never beengit add-ed, usegit clean -fd.- Remote tracking:
git switch -c feature origin/featureautomatically sets up upstream tracking.
# Switch branch and discard all uncommitted changes
git switch --discard-changes develop
# Delete untracked files and directories
git clean -fd
# Create local branch from remote with tracking
git switch -c feature/api-v2 origin/feature/api-v2
After two months on this workflow, the number of accidental detached HEAD states and lost changes from mistyped commands dropped noticeably. Not because people were being more careful — the new syntax simply doesn’t allow those mistakes to happen accidentally. On top of that, reading git commands in PR descriptions or commit messages became much clearer.
git checkout is still there and won’t be going away. But if you’re starting a new project or onboarding new team members, start with git switch and git restore from day one. Less to remember, fewer edge cases, and you’ll never have to explain what “detached HEAD state” is again.
