How do I perform a hard reset on a single file in Git to discard changes and overwrite it with a fresh HEAD copy?
To perform a hard reset on a single file in Git and discard all local changes, you need to use git checkout HEAD <filename> or git checkout -- <filename>. This command will overwrite the working file with the version from the HEAD commit, effectively discarding all local modifications to that specific file.
Contents
- Understanding Git Reset vs Checkout
- The Correct Command for Single File Hard Reset
- Step-by-Step Process
- Alternative Approaches
- Important Considerations
- Practical Examples
- Advanced Scenarios
Understanding Git Reset vs Checkout
The confusion about hard resetting a single file stems from understanding how Git’s reset and checkout commands work differently at the file level.
Git reset with file paths behaves differently than with commits:
git reset HEAD <filename>only unstages the file from the staging area- It does not affect the working directory - your local changes remain intact
- The
--soft,--mixed, and--hardflags do not work with file-level reset commands source
Git checkout with file paths:
git checkout HEAD <filename>overwrites the working file with the version from HEAD- This effectively performs a “hard reset” for that specific file
- It discards all local changes to the file and replaces it with the HEAD version
Key Insight: Unlike
git resetwhich operates on commits and branches,git checkoutcan operate on individual files in the working directory, making it the correct tool for discarding changes to specific files.
The Correct Command for Single File Hard Reset
The primary command for hard resetting a single file is:
git checkout HEAD <filename>
Or equivalently:
git checkout -- <filename>
Both commands achieve the same result - they overwrite the working file with the version from HEAD, discarding all local changes.
Why this works:
git checkoutcan update individual files in the working directory- When given a file path and a commit reference (HEAD), it replaces the working file with the version from that commit
- This effectively performs a hard reset operation at the file level
Step-by-Step Process
Here’s the complete process to hard reset a single file:
-
Check the current status:
bashgit status
-
Perform the hard reset on the specific file:
bashgit checkout HEAD <filename>
Or:
bashgit checkout -- <filename>
-
Verify the changes were discarded:
bashgit status git diff <filename>
-
Add the file back to staging if needed:
bashgit add <filename>
Example: If you want to reset config.json:
git checkout HEAD config.json
Alternative Approaches
Using git restore (Git 2.23+)
For newer versions of Git (2.23 and later), you can use git restore:
git restore <filename>
This is the modern equivalent of git checkout -- <filename> and is more explicit about its purpose of restoring files.
Two-step process with reset and checkout
Some sources suggest a two-step process:
git reset HEAD <filename> git checkout HEAD <filename>
However, the first command is unnecessary since git checkout HEAD <filename> already handles both unstaging and restoring the file in one operation.
Important Considerations
Head vs Branch References
git checkout HEAD <filename>always uses the latest commit on the current branch- If you want to get changes from a different commit, specify it explicitly:bash
git checkout <commit-hash> <filename>
File Deletion Handling
git checkoutwill not restore a file if it was deleted in the source commit- If the file doesn’t exist in HEAD, the command will fail
- Use
git checkout HEAD -- <filename>syntax to be explicit about restoring from HEAD
Staged vs Unstaged Changes
Both staged and unstaged changes to the file will be discarded when you use git checkout HEAD <filename>.
Detached HEAD Risk
Unlike git reset <commit> which can put you in a detached HEAD state, git checkout HEAD <filename> does not affect your branch pointer - you remain on your current branch.
Practical Examples
Example 1: Discarding local changes to a file
# Make some changes to app.js
echo "console.log('new code');" >> app.js
# Check status - shows modified file
git status
# On branch main
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git restore <file>..." to discard changes in working directory)
# modified: app.js
# Hard reset the single file
git checkout HEAD app.js
# Verify changes are discarded
git status
git diff app.js
Example 2: Resetting multiple files
# Reset multiple files at once
git checkout HEAD file1.txt file2.json config.yaml
Example 3: Using with a specific commit
# Reset file to version from 3 commits ago
git checkout HEAD~3 important.js
Advanced Scenarios
Handling Submodules
If you’re working with submodules, you may need additional flags:
git checkout HEAD --recurse-submodules <filename>
Interactive Reset
For more complex scenarios, you can use interactive checkout:
git checkout -p HEAD <filename>
This allows you to selectively discard changes line by line.
Safety Check Before Reset
Before performing a hard reset, you might want to save important changes:
# Stash changes before reset
git stash push -m "Before hard reset"
# Perform the reset
git checkout HEAD <filename>
# Review changes
git stash show
# If everything looks good, drop the stash
git stash drop
# Or if you need the changes back
git stash pop
Remember that git checkout HEAD <filename> is the correct and most direct way to perform a hard reset on a single file in Git. Unlike git reset which operates at the commit level, git checkout can work with individual files in your working directory, making it the perfect tool for discarding changes to specific files while keeping your repository history intact.