How to Discard Unstaged Changes in Git
You've been experimenting with code changes, but they didn't work out as planned, or you want to start fresh from your last commit. Git provides several commands to discard unstaged changes and return your working directory to a clean state.
In this guide, you'll learn different methods to discard unstaged changes, from individual files to entire directory trees.
Prerequisites
You need Git installed on your system and basic knowledge of Git's working directory and staging area concepts. You should be working in a Git repository with some unstaged changes to practice these operations.
Understanding Unstaged Changes
Unstaged changes are modifications in your working directory that haven't been added to Git's staging area. These include:
- Modified tracked files
- New untracked files
- Deleted files
Git treats each type differently when discarding changes.
Discarding Changes in Tracked Files
Using git checkout (Traditional Method)
The traditional way to discard changes in tracked files:
# Discard changes in a specific file
git checkout -- filename.js
# Discard changes in multiple files
git checkout -- file1.js file2.css file3.html
# Discard all changes in tracked files
git checkout -- .
# Discard changes in a specific directory
git checkout -- src/components/
The --
separator ensures Git treats the following arguments as file paths, not branch names.
Using git restore (Modern Method)
Git 2.23 introduced git restore
as a more intuitive replacement:
# Restore a specific file to its committed state
git restore filename.js
# Restore multiple files
git restore file1.js file2.css
# Restore all modified files
git restore .
# Restore files in a specific directory
git restore src/components/
The git restore
command is clearer in its intent compared to git checkout
.
Checking What Will Be Discarded
Before discarding changes, review what you're about to lose:
# See all unstaged changes
git status
# View detailed changes in modified files
git diff
# See changes in a specific file
git diff filename.js
# Show names of modified files only
git diff --name-only
Always review your changes before discarding them, as this operation cannot be undone.
Discarding Specific Types of Changes
Modified Files Only
To discard only modifications while keeping new files:
# Using git restore
git restore .
# Using git checkout
git checkout -- .
# These commands only affect tracked files, leaving untracked files untouched
New Untracked Files
To remove untracked files, use git clean
:
# Preview what files will be deleted (dry run)
git clean -n
# Remove untracked files
git clean -f
# Remove untracked files and directories
git clean -fd
# Remove untracked files including ignored files
git clean -fx
Warning: git clean
permanently deletes files. Always use -n
first to preview.
Deleted Files
To restore deleted files:
# Restore a deleted file
git restore deleted-file.js
# Restore all deleted files
git restore .
# Check which files were deleted
git status | grep deleted
Interactive and Selective Discarding
Interactive File Selection
For precise control over which changes to discard:
# Interactively select changes to discard
git restore -p filename.js
# This allows you to review each change and decide:
# y - discard this change
# n - keep this change
# q - quit and keep remaining changes
# s - split change into smaller parts
Discarding Specific Lines
Using interactive mode, you can discard individual changes within files:
# Start interactive restore for multiple files
git restore -p .
# Review each change and choose what to discard
# This is useful when you have both good and bad changes in the same file
Handling Different File States
Mixed Changes (Staged and Unstaged)
When files have both staged and unstaged changes:
# Check file states
git status
# Output might show:
# modified: src/app.js (staged changes)
# modified: src/app.js (unstaged changes)
# Discard only unstaged changes
git restore src/app.js
# The staged changes remain ready for commit
Dealing with File Conflicts
If you have merge conflicts that you want to abandon:
# Discard conflict resolution attempts
git restore --conflict=merge filename.js
# Or start over by restoring to HEAD state
git restore --source=HEAD filename.js
Bulk Operations
Discarding All Unstaged Changes
Clean slate approach - discard everything unstaged:
# Discard all unstaged changes in tracked files
git restore .
# Remove all untracked files and directories
git clean -fd
# Verify clean state
git status
# Should show: "nothing to commit, working tree clean"
Pattern-Based Discarding
Discard changes matching specific patterns:
# Discard changes in all JavaScript files
git restore "*.js"
# Discard changes in all files in src directory
git restore "src/**"
# Discard changes in test files
git restore "*test*"
Directory-Specific Operations
Work with specific directories:
# Discard changes in specific directory
git restore src/components/
# Remove untracked files in specific directory
git clean -fd src/temp/
# Restore deleted directory
git restore deleted-directory/
Advanced Scenarios
Restoring from Specific Commits
Sometimes you want to discard changes by reverting to a specific commit state:
# Restore file to state from specific commit
git restore --source=HEAD~2 filename.js
# Restore file to state from specific commit hash
git restore --source=a1b2c3d filename.js
# Restore multiple files from specific commit
git restore --source=HEAD~1 src/
Selective Restoration
Restore specific parts of files from different commits:
# Interactively restore from a specific commit
git restore --source=HEAD~1 -p filename.js
# This allows you to selectively take parts of the file from that commit
Safety and Recovery
Creating Backups Before Discarding
For important changes you might want to recover later:
# Create a patch file of your changes
git diff > my-changes.patch
# Or stash changes instead of discarding
git stash push -m "Backup before discarding other changes"
# Then discard what you don't want
git restore problematic-file.js
# Apply stash later if needed
git stash pop
Viewing Discarded Changes
Unfortunately, once changes are discarded with restore
or checkout
, they're gone. However, you can:
# Check reflog for any commits that might contain the changes
git reflog
# If you had committed the changes at some point, you might find them there
Best Practices
Review Before Discarding
Always check what you're about to lose:
# Good workflow:
git status # See what's modified
git diff # Review all changes
git diff filename.js # Review specific file changes
git restore filename.js # Then discard
Use Selective Operations
Instead of discarding everything, be selective:
# Instead of git restore .
# Discard files individually
git restore problematic-file.js
git restore another-bad-file.css
# Keep files with good changes
Combine with Stashing
Use stashing for temporary storage:
# Stash good changes
git add good-changes.js
git stash push --staged -m "Good changes to keep"
# Discard everything else
git restore .
# Apply good changes back
git stash pop
Common Workflows
Quick Clean Reset
When you want to start completely fresh:
# Complete reset to last commit state
git restore . # Discard modified files
git clean -fd # Remove untracked files
git status # Verify clean state
Selective Cleanup
When you want to keep some changes:
# Review changes
git status
git diff
# Keep good files by staging them
git add good-file1.js good-file2.css
# Discard everything else
git restore .
# Your good changes are still staged
git status
Now you understand how to effectively discard unstaged changes in Git. These techniques help you maintain a clean working directory and recover from experimental changes that didn't work out as planned.
Found an issue?