How-to

git checkout vs switch vs restore: When to Use Each

Updated: · Skillaria.Top

git checkout does two different jobs: switching branches and restoring files, so since Git 2.23 those jobs are split between git switch (branches) and git restore (files). Use git switch name to change branches, git restore file to discard uncommitted changes in a file, and keep checkout for old scripts and familiar workflows.

Why git checkout does two jobs

git checkout has historically performed two fundamentally different operations. Given a branch name, it moves HEAD and updates the working directory. Given a file path, it takes the file's version from the index or a commit and silently overwrites your changes. The same command either safely moves between branches or irreversibly throws away unsaved work.

That's why Git 2.23 introduced two separate commands: git switch for branches and git restore for files. Checkout hasn't been removed and works perfectly well, but new code, documentation and Git's own hints use switch and restore. If you're just learning, start with those; you still need to know checkout, because it shows up in tutorials, scripts and job interviews.

Command mapping

Taskgit checkoutNew command
Switch to a branchgit checkout maingit switch main
Create a branch and switch to itgit checkout -b featuregit switch -c feature
Create or reset a branchgit checkout -B featuregit switch -C feature
Go back to the previous branchgit checkout -git switch -
Check out a commit (detached HEAD)git checkout 3f2a1c9git switch --detach 3f2a1c9
Discard changes in a filegit checkout -- app.jsgit restore app.js
Unstage a filegit reset HEAD app.jsgit restore --staged app.js
Get a file from another branch or commitgit checkout main -- app.jsgit restore --source=main --staged --worktree app.js
Discard all changes in a directorygit checkout -- .git restore .

Note the -- in the checkout commands. It's a separator meaning "paths follow". It prevents ambiguity when a file and a branch share the same name. Restore doesn't have this problem because it only works with files.

git checkout for branches

git checkout feature/login        # switch
git checkout -b feature/login     # create from the current commit and switch
git checkout -b hotfix origin/main   # create from a remote branch
git checkout -t origin/feature/x  # local branch tracking the remote one

If there's no local branch but the server has a branch with that name, git checkout feature/x (just like git switch feature/x) creates a local branch and sets up tracking automatically. You need a git fetch first, otherwise Git doesn't know about the new remote branch.

Switching doesn't lose uncommitted changes: they carry over to the other branch as long as they don't conflict with it. If they do, Git refuses and suggests committing or stashing them with git stash. The -f (--force) flag switches anyway and discards local edits, which is a dangerous operation.

git switch: branches only

Switch mirrors the branch side of checkout but with stricter rules. The key difference: it won't check out a commit or tag without an explicit --detach. git switch v1.0 fails with an error instead of quietly putting you in detached HEAD, so it's harder to end up "off a branch" by accident.

git switch main
git switch -c feature/cart
git switch -c feature/cart origin/main
git switch --detach v1.0
git switch -              # previous branch
git switch -m main        # carry edits over with a three-way merge

git restore: the undo button for files

Restore brings files back to a saved state and never touches commit history. To use it confidently, keep Git's three areas in mind: the working directory (what you see in your editor), the index (what you've staged with git add) and commits.

How to discard changes in a file before git add

git restore app.js         # restore the version from the index
git restore src/           # every file in the directory
git restore .              # every modified file in the project

Without flags, restore restores the working directory from the index. If the file hasn't been staged since the last commit, that's the same as the version from the last commit. The changes vanish without a trace and don't go to any trash bin, so check git diff before running git restore ..

How to unstage a file after git add

git restore --staged app.js

The file is no longer staged for commit, but your edits stay in it. This is the safest form of restore: nothing is lost. It's exactly the command git status suggests under "Changes to be committed".

How to discard everything: both staged and in the file

git restore --staged --worktree app.js
# short form
git restore -SW app.js

How to restore a file from another commit or branch

git restore --source=HEAD~2 app.js          # version from two commits ago
git restore --source=main config.yml        # version from the main branch
git restore --source=3f2a1c9 -- docs/       # a whole directory from a commit

By default, --source changes only the working directory. Add --staged if you want the change staged right away. The old form git checkout main -- app.js always updates both the index and the working directory.

How to restore a deleted file

git restore deleted.txt                          # deleted but not yet committed
git restore --source=HEAD~1 deleted.txt          # deletion already committed

If you don't know which commit deleted the file, find it: git log --oneline -- deleted.txt. The latest commit in the list is the one that deleted the file, so restore from its parent: git restore --source=<hash>~1 deleted.txt.

restore vs reset vs revert

All three "undo" things, but at different depths:

  • restore works on files in the working directory and index and doesn't change history. Safe for teams.
  • reset moves the branch pointer back, rewriting history. --soft keeps the changes staged, --mixed (the default) keeps them in the working directory, --hard deletes them. Dangerous for commits already pushed to shared branches.
  • revert creates a new commit that undoes the given one. History isn't rewritten, so it's the right way to undo a commit that's already on the server.

Rule of thumb: changes not committed yet, use restore; commit only local, use reset; commit already shared with teammates, use revert.

Detached HEAD after checkout

After git checkout 3f2a1c9 or git checkout v1.0 you'll see a long warning: "You are in 'detached HEAD' state". HEAD now points to a commit rather than a branch. That's handy for looking at an old version or running tests against it.

You can commit in this state, but new commits won't belong to any branch. If you changed something and want to keep it:

git switch -c fix-for-v1

If you were just looking, go back: git switch - or git switch main. If you left without creating a branch, the commits are still reachable via git reflog: find the hash and run git branch rescue <hash>.

Common errors

error: pathspec 'feature' did not match any file(s) known to git

Git found neither a branch nor a file with that name. Usually the branch exists only on the server and you haven't fetched. Run git fetch and try again. Also check the spelling: branch names are case-sensitive.

Accidentally discarded changes with checkout -- or restore

Git doesn't keep uncommitted edits that were never staged, so you can't get them back with Git. Your editor's or IDE's local history is your only hope. If the file had been added with git add, though, its contents are in the object database as a dangling object and can be found with git fsck --lost-found.

A file and a branch with the same name

git checkout test with both a test branch and a test directory prints an ambiguity warning. Use git switch test for the branch and git restore test (or git checkout -- test) for the files.

Everyday scenarios

A few situations that come up all the time, and the right command for each:

  • You started editing a file and realized you're on the wrong track: git restore file.
  • You staged an extra file before committing: git restore --staged file.
  • You need to jump to another task mid-work: git stash, then git switch branch; when you're back, git switch - and git stash pop.
  • You want one file from a neighboring branch without merging the whole branch: git restore --source=feature/x --staged --worktree path.
  • You need to check whether a bug existed in an older version: git switch --detach v1.0, test, then git switch -.
  • You want to undo only part of the changes in a file: git restore -p file. Git shows each hunk and asks whether to discard it.

Interactive mode -p (--patch) is especially useful: both checkout and restore support it, and it lets you drop debug output while keeping useful edits in the same file.

How to review changes before discarding them

Restore and checkout with paths throw changes away without confirmation, so it pays to look at what you're about to lose first. git diff shows working directory changes against the index, git diff --staged shows what's staged for commit, and git diff main -- app.js shows how the file differs from the version in another branch. If in doubt, save the current state with git stash: unlike restore, you can always get changes back from a stash.

Summary

git checkout combines switching branches and restoring files, and that's exactly what makes it dangerous. Use git switch for branches and git restore for files: one only changes where HEAD points, the other only changes file contents. Branches, restore, reset and revert, with good and bad scenarios, are covered hands-on in the Git course.

FAQ

Is git checkout deprecated?

No, it hasn't been deprecated and will keep working. For everyday work, the narrower and safer git switch and git restore are simply recommended.

Which Git version added switch and restore?

Both commands arrived in Git 2.23 (2019). Check your version with git --version.

Can I undo git restore?

If it restored the working directory, Git didn't keep the discarded edits, so it can't be undone. git restore --staged is safe: the edits stay in the file and you just need to git add them again.

How do I switch to a teammate's branch?

Run git fetch, then git switch branch-name: Git creates a local branch tracking origin/branch-name.

What's the difference between git checkout . and git reset --hard?

git checkout . (or git restore .) reverts modified files to the index version without touching the index or history. git reset --hard resets both the index and the working directory to a commit, and with a commit argument also moves the branch.

Learn this topic on video

This topic is covered in depth with practice in the course “Git from Scratch to Confident Developer”.

Go to course