Master the art of undoing changes in Git
Git reset is a powerful command for undoing changes. This comprehensive guide covers all essential git reset commands with practical examples and safety information.
Moves HEAD pointer but keeps changes staged. Perfect for editing last commit message or combining commits.
$ git commit -m "Initial commit"
$ git add forgotten_file.txt
$ git reset --soft HEAD~1
# Keeps both changes staged
$ git commit -m "Complete initial commit"
[main abc1234] Complete initial commit
Moves HEAD pointer, unstages changes (default --mixed). Use to unstage files while keeping changes.
$ git add .
Changes to be committed: file1.txt, file2.txt
$ git reset HEAD
# Unstage all files
Unstaged changes after reset: file1.txt, file2.txt
$ git reset file.txt
# Unstage specific file
Unstaged changes after reset: file.txt
Moves HEAD pointer, discards all changes. Completely removes the last commit. Use with extreme caution!
$ git commit -m "Buggy commit"
[main def5678] Buggy commit
$ git reset --hard HEAD~1
# Completely removes the commit
HEAD is now at abc1234 Previous good commit
Discard unstaged changes in a specific file. File-level undo that doesn't affect other files.
$ git status
modified: file.txt
$ git checkout -- file.txt
$ git status
nothing to commit, working tree clean
Revert one file to its state at a specific commit. Useful for selectively undoing changes to a single file.
$ git log --oneline -- file.txt
def5678 Update file.txt
abc1234 Initial version
$ git reset abc1234 -- file.txt
# Revert file.txt to abc1234 state
Selectively undo specific lines from last commit. Interactive patch mode for precise control.
$ git reset -p HEAD~1 -- file.txt
diff --git a/file.txt b/file.txt
index 1234567..89abcde 100644
--- a/file.txt
+++ b/file.txt
@@ -1,5 +1,5 @@
-Hello World
+Hello Git
Discard this hunk? [y,n,q,a,d,/,j,J,g,e,?]? y
Unstaged changes after reset: M file.txt
Reset local branch to match remote exactly. Destructive operation that discards all local changes.
$ git fetch origin
remote: Counting objects: 5, done.
remote: Total 5 (delta 3), reused 5 (delta 3)
$ git reset --hard origin/main
HEAD is now at def5678 Latest commit from remote
# WARNING: All local changes will be lost!
Reset all txt files using pathspec. Useful for unstaging groups of files by pattern.
$ git add '*.txt'
$ git status
Changes to be committed: file1.txt, file2.txt
$ git reset '*.txt'
$ git status
Changes not staged for commit: file1.txt, file2.txt
Abort a merge but keep changes. Useful when you want to stop a merge and try a different approach.
$ git merge feature-branch
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
$ git reset --merge
Merge aborted, changes preserved
$ git status
On branch main, nothing to commit
$ git reset --keep HEAD~1
HEAD is now at abc1234 Previous commit
$ git status
Changes not staged for commit: file1.txt, file2.txt
# Local changes are preserved
Nuclear reset - makes working directory match last commit exactly. Removes all changes and untracked files.
$ git reset --hard
HEAD is now at abc1234
$ git clean -fd
Removing untracked_file.txt
Removing untracked_dir/
# WARNING: This cannot be undone!
Remove commits from shared history. Requires force push and should only be used on private branches.
$ git reset --hard HEAD~5
HEAD is now at def5678
$ git push --force
+ abc1234...def5678 main -> main (forced update)
# WARNING: This rewrites public history!
Time machine reset to a specific moment. Powerful but dangerous - can cause data loss.
git reset --hard "@{9am yesterday}..@{5pm yesterday}" # Undo all yesterday's work
$ git reset --hard "@{2024-03-15 14:30:00 -0400}"
HEAD is now at abc1234 Commit from that time
# Use git reflog to recover if needed
Keep working changes while resetting commits. Preserves uncommitted changes during history manipulation.
$ git stash
Saved working directory and index state
$ git reset --hard HEAD~3
HEAD is now at abc1234
$ git stash pop
Restored working changes
Split a commit into multiple logical changes. Powerful technique for creating clean commit history.
$ git reset --soft HEAD~3
# Rewind 3 commits, keep changes staged
$ git reset -p
# Interactive partial unstage
Discard this hunk? [y,n,q,a,d,/,j,J,g,e,?]? y
$ git commit -m "Feature part 1"
$ git commit -m "Feature part 2"
$ git commit -m "Feature part 3"
Precise branch resynchronization while preserving staged changes. Advanced technique for complex workflows.
$ git fetch origin
$ git reset --hard origin/main --keep-index
# Reset branch but preserve staged changes
$ git stash
# Stash working changes
$ git stash apply
# Reapply working changes
CI/CD safe reset pattern. Uses --force-with-lease which is safer than --force for automated environments.
# In CI/CD pipeline script
git reset --hard $FAILED_COMMIT
HEAD is now at abc1234 Failed commit
git push --force-with-lease
To github.com:user/repo.git
+ def5678...abc1234 main -> main
# Safer than --force as it checks remote state
Standard unstage command. Use to unstage all files while keeping changes in working directory.
$ git add .
Changes to be committed: file1.txt, file2.txt
$ git reset --mixed HEAD
Unstaged changes after reset: file1.txt, file2.txt
$ git status
Changes not staged for commit: file1.txt, file2.txt
Recover from mistaken reset using reflog. Find the commit hash from before the reset and restore to it.
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~1
def5678 HEAD@{1}: commit: Important work
$ git reset --hard def5678
HEAD is now at def5678 Important work
# Recovery successful!
Rewind the entire repository to its state from exactly one day ago. Powerful but dangerous - can cause data loss.
$ git log --oneline -3
abc1234 (HEAD -> main) Today's work
def5678 Yesterday's work
ghi9012 Work from two days ago
$ git reset --hard @{1.day.ago}
HEAD is now at def5678 Yesterday's work
# Repository is now exactly as it was 24 hours ago
$ git status
HEAD detached at def5678
nothing to commit, working tree clean
Retrieve a specific file from 5 commits ago without affecting the rest of your working directory.
$ git log --oneline -7 -- file.txt
abc1234 Current version
def5678 Removed feature X
ghi9012 Added feature Y
jkl3456 Added feature X
# Restore file.txt to its state from 5 commits ago
$ git checkout HEAD@{5} -- file.txt
$ git status
Changes not staged for commit: file.txt
# file.txt now contains its content from 5 commits ago
Replace the current HEAD commit with a new one. Useful for fixing commit messages or adding forgotten changes.
$ git commit -m "Initial implmentation"
[main abc1234] Initial implmentation
# Oops, typo in commit message and forgot a file
$ git add forgotten-file.txt
$ git commit --amend
# Editor opens to fix commit message
[main def5678] Initial implementation
# Commit hash changed, message fixed, file added
Search through reflog with relative dates to find lost commits by their commit message content.
$ git reflog --relative-date | grep "commit: Add"
abc1234 HEAD@{2 days ago}: commit: Add user
authentication
def5678 HEAD@{3 days ago}: commit: Add database schema
ghi9012 HEAD@{5 days ago}: commit: Add initial project
structure
# Found the commit where authentication was added
$ git show abc1234
commit abc1234...
Author: John Doe
Date: 2 days ago
Add user authentication
Complex command to reset to the first checkout operation found in reflog. Advanced recovery technique.
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~1
def5678 HEAD@{1}: checkout: moving from feature to main
ghi9012 HEAD@{2}: commit: Important work on feature
# Extract the first checkout reference
$ git reset --hard HEAD@{git reflog | grep -m1 "checkout:" |
cut -d' ' -f1}
HEAD is now at def5678
# Now at the state right after the checkout operation
Undo the last 3 commits but keep all changes staged. First step in a multi-level undo operation.
$ git log --oneline -4
abc1234 (HEAD -> main) Commit 3
def5678 Commit 2
ghi9012 Commit 1
jkl3456 Base commit
$ git reset --soft HEAD~3
HEAD is now at jkl3456 Base commit
$ git status
Changes to be committed: (all changes from last 3
commits)
Undo 2 more commits, unstaging the changes. Second step in a multi-level undo operation.
# Continuing from previous reset --soft
$ git reset HEAD~2
Unstaged changes after reset: (changes from last 2
commits)
$ git status
Changes not staged for commit: (changes from last 2
commits)
Changes to be committed: (changes from first commit only)
Final step: completely discard all changes. Use with caution as this cannot be undone.
# Continuing from previous reset
$ git reset --hard HEAD~1
HEAD is now at mno7890 (earlier than base commit)
$ git status
nothing to commit, working tree clean
# All changes from the last 3 commits are now gone
Standard unstage command. Resets the index to match HEAD while keeping working directory changes.
$ git add .
Changes to be committed: file1.txt, file2.txt
$ git reset --mixed HEAD
Unstaged changes after reset: file1.txt, file2.txt
$ git status
Changes not staged for commit: file1.txt, file2.txt
# Files are unstaged but changes are preserved
Reset all skip-worktree flags. Useful when you want to start tracking previously ignored files again.
$ git ls-files -v | grep '^S'
S config/local.properties
S temp/test-data.json
# These files have skip-worktree flag set
$ git ls-files -z | xargs -0 git update-index
--no-assume-unchanged
$ git ls-files -v | grep '^S'
# No output - all skip-worktree flags are cleared
$ git status
Changes not staged for commit: config/local.properties,
temp/test-data.json
Abort a merge and reset to ORIG_HEAD (state before merge began). Preserves your working directory changes.
$ git merge feature-branch
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the
result.
# Changed your mind about the merge
$ git reset --merge ORIG_HEAD
Unstaged changes after reset: file.txt
$ git status
Changes not staged for commit: file.txt
no changes added to commit
# Merge aborted, back to pre-merge state
Alternative to --merge with better change preservation. Resets index to specified commit but keeps working tree changes.
$ git reset --keep HEAD~1
Unstaged changes after reset: file1.txt, file2.txt
$ git status
Changes not staged for commit: file1.txt, file2.txt
no changes added to commit
# Working tree changes preserved, index reset to HEAD~1
Always check git status and git log before
using reset commands, especially --hard. Consider using
git revert for public commits instead of resetting, as it
doesn't rewrite history.
Use git reflog to recover from mistaken resets - it keeps
a history of all reference changes for 90 days by default.
When using advanced reset techniques, always:
git status and git log before
performing resets
git reflog to recover from mistaken resets