You have used git checkout to switch branches. But this command is far more powerful than a branch switcher. It is your tool for viewing the past, recovering deleted files, and retrieving specific versions of files from any point in history.
Understanding checkout's full capabilities means you can recover from almost any mistake. Accidentally deleted a file? Checkout brings it back. Need to see how the code looked last week? Checkout takes you there. Want to grab just one file from another branch? Checkout does that too.
git checkout . or git checkout -- . to "clean up" your working directory. This permanently discards all uncommitted changes in every file. It is the file-level equivalent of git reset --hard and just as dangerous if you have unsaved work.Restoring files to previous versions
Restore to the last commitWhat is commit?A permanent snapshot of your staged changes saved in Git's history, identified by a unique hash and accompanied by a message describing what changed.
You made changes to a file but want to discard them entirely:
# Discard local changes to app.js
git checkout -- app.js
# Modern equivalent (more explicit)
git restore app.jsThis permanently overwrites your local modifications. The file reverts to exactly how it was at the last commit.
Restore to a specific commit
You want a file as it existed several commits ago:
# Get app.js as it was in commit abc1234
git checkout abc1234 -- app.js
# Git stages the restored version automatically
git status
# Changes to be committed:
# modified: app.js
# Commit the restoration
git commit -m "revert: restore app.js to stable version"This does not move you to that commit. You stay on your current branch. Only that specific file changes.
Restore multiple files or directories
# Restore an entire directory
git checkout abc1234 -- src/components/
# Restore multiple specific files
git checkout HEAD -- package.json package-lock.json
# Restore all files (dangerous, discards all changes)
git checkout abc1234 -- .git restore --staged file.Detached HEADWhat is detached head?A Git state where you're viewing a specific commit directly instead of being on a branch, meaning any new commits will be orphaned when you switch away. state
Checking out a commitWhat is commit?A permanent snapshot of your staged changes saved in Git's history, identified by a unique hash and accompanied by a message describing what changed. directly (instead of a branch) puts you in "detached HEAD" state:
git checkout abc1234Git warns you:
You are in 'detached HEAD' state. You can look around, make
experimental changes and commit them, and you can discard any
commits you make in this state without impacting any branches.What you can do in detached HEAD:
- View the code as it was at that commit
- Run tests, build the project
- Look around to understand what changed
What you should not do:
- Make commits (they will be orphaned and eventually garbage collected)
Getting back:
git checkout mainCreating a branch from an old commit
If you want to actually work from a historical point, create a branch:
# Create a new branch starting from an old commit
git checkout -b hotfix-v1 abc1234Now you are on a proper branch and can commit safely.
Recovering a deleted file
# You accidentally deleted config.js
git status
# D config.js
# Restore it from the last commit
git checkout HEAD -- config.js
# If you already committed the deletion, go back further
git log --oneline -- config.js
# e4f5g6h Delete config
# a1b2c3d Update config
# Restore from the commit before deletion
git checkout a1b2c3d -- config.jsCheckout vs restore vs switch vs reset
Modern Git splits the overloaded checkout command into specialized tools:
| Task | Old command | Modern command |
|---|---|---|
| Switch branches | git checkout branch | git switch branch |
| Create and switch | git checkout -b branch | git switch -c branch |
| Restore a file | git checkout -- file | git restore file |
| Unstage a file | git reset HEAD file | git restore --staged file |
| Restore from specific commit | git checkout commit -- file | git restore --source=commit file |
Both the old and modern commands work. The modern versions are clearer about intent, which reduces the chance of accidentally doing the wrong thing.
Practical workflows
Code archaeology: finding when a bug appeared
# Go back 10 commits and test
git checkout HEAD~10
# Run tests... bug not present
# Go back 5 commits
git checkout HEAD~5
# Bug present! Narrow down further
# Return to main when done
git checkout mainCherry-picking a file from another branch
# Grab one file from another branch without merging
git checkout feature-branch -- src/utils/helpers.js
git commit -m "feat: add helper functions from feature-branch"Quick reference
| Command | Purpose |
|---|---|
git checkout -- file | Restore file to last commit (destructive) |
git checkout commit -- file | Restore file to specific commit |
git checkout commit | View old commit (detached HEAD) |
git checkout -b branch commit | Create branch from old commit |
git restore file | Modern: restore file to last commit |
git switch branch | Modern: switch branches |