Skip to main content
git reset is a powerful command for undoing changes by moving the current branch pointer to a different commit. Understanding its modes is crucial for safe operation.
git reset --hard permanently discards changes. Make sure you have backups or that you truly want to lose those changes before using it.

What Does Reset Do?

Reset changes which commit your current branch (HEAD) points to. Depending on the mode, it can also update:
  1. The index (staging area)
  2. The working directory (your files)

Reset Modes

—soft: Keep Everything

Moves HEAD only. Index and working directory unchanged.
git reset --soft HEAD~1
Use case: Undo a commit but keep all changes staged. Example:
# Combine last 5 commits into one
git reset --soft HEAD~5
git commit -m "Combined feature implementation"

—mixed: Default Behavior

Moves HEAD and updates the index. Working directory unchanged.
git reset HEAD~1
# Equivalent to:
git reset --mixed HEAD~1
Use case: Undo commits and unstage changes, but keep modifications in your working directory. Example:
# Unstage all files
git reset

# Unstage specific files
git reset -- file.txt

—hard: Discard Everything

Moves HEAD, updates index and working directory. Destructive!
git reset --hard HEAD~1
Use case: Completely discard commits and all changes.
This permanently deletes uncommitted changes. Tracked files not in the target commit are removed.
Example:
# Discard all local changes and commits
git reset --hard origin/main

—merge: Safe During Merges

Resets index and updates files different between commit and HEAD, but keeps local changes.
git reset --merge HEAD~1
Use case: Abort a merge while preserving local modifications.

—keep: Preserve Local Changes

Resets index and updates working tree, but aborts if local changes would be lost.
git reset --keep HEAD~1
Use case: Safely remove commits while keeping your work-in-progress changes.

Reset Modes Comparison

ModeHEADIndexWorking DirSafety
--softSafe
--mixedSafe
--hardDangerous
--mergePartialSafe
--keepPartialSafe

Common Operations

Unstaging Files

Remove files from the staging area:
# Unstage all files
git reset

# Unstage specific file
git reset -- path/to/file.txt

# Alternative (newer syntax)
git restore --staged path/to/file.txt

Undoing the Last Commit

1
Keep changes staged
2
git reset --soft HEAD~1
3
Keep changes but unstage them
4
git reset HEAD~1
5
Discard changes completely
6
git reset --hard HEAD~1

Undoing Multiple Commits

# Undo last 3 commits, keep changes
git reset HEAD~3

# Undo commits, discard all changes
git reset --hard HEAD~3

Moving to a Specific Commit

# Reset to a specific commit
git reset --hard abc123

# Reset to where you were 2 moves ago
git reset --hard HEAD@{2}

Detailed Examples

Undo Add (Unstage)

$ git add frotz.c filfre.c
$ git reset                    # Unstage both files
$ git add filfre.c             # Stage only the one you want
$ git commit

Undo Commit and Redo

$ git commit -m "Incomplete work"
$ git reset --soft HEAD^       # Undo commit, keep changes staged
$ # Edit files to fix them
$ git commit -a -c ORIG_HEAD   # Commit with the old message

Create Topic Branch from Commits

$ git branch topic/wip         # Save current position
$ git reset --hard HEAD~3      # Remove last 3 commits from main
$ git switch topic/wip         # Continue work on topic branch

Undo a Merge

$ git merge feature
# Conflicts! Not ready to deal with this now
$ git reset --hard             # Abort the merge
$ git reset --hard ORIG_HEAD   # Alternative: undo completed merge

Undo Merge Keeping Local Changes

$ git pull
# After inspecting, you don't want these changes
$ git reset --merge ORIG_HEAD  # Undo merge, preserve local changes

Split a Commit

1
Reset to before the commit
2
git reset -N HEAD^
3
The -N flag marks files as intent-to-add.
4
Interactively stage changes
5
git add -p
6
Commit each logical change
7
git commit -m "First logical change"
git add -p
git commit -m "Second logical change"
8
Continue until all changes are committed
9
git add .
git commit -m "Remaining changes"

Understanding the Three Trees

Git manages three trees:
  1. HEAD - Last commit snapshot, next parent
  2. Index - Proposed next commit snapshot (staging area)
  3. Working Directory - Your actual files

State Table Example

Given states A, B, C, D:
working  index  HEAD  target    --soft    --mixed   --hard
   A       B      C      D         A  B  D   A  D  D   D  D  D
   A       B      C      C         A  B  C   A  C  C   C  C  C
   B       B      C      D         B  B  D   B  D  D   D  D  D

Recovering from Reset

ORIG_HEAD Reference

Git saves the previous HEAD position in ORIG_HEAD:
# Undo a reset
git reset --hard ORIG_HEAD

Using Reflog

Find lost commits in the reflog:
# View reflog
git reflog

# Output:
# abc123 HEAD@{0}: reset: moving to HEAD~3
# def456 HEAD@{1}: commit: Add feature

# Recover to before the reset
git reset --hard HEAD@{1}

Reset vs. Other Commands

Reset vs. Revert

AspectResetRevert
HistoryRewritesCreates new commit
SafetyDangerous on shared branchesSafe for shared branches
Use caseLocal cleanupUndo public commits
# Reset (rewrites history)
git reset --hard HEAD~1

# Revert (preserves history)
git revert HEAD

Reset vs. Checkout vs. Restore

CommandMoves HEADUpdates IndexUpdates Working Dir
reset --softYesNoNo
reset --mixedYesYesNo
reset --hardYesYesYes
checkout <commit>Yes (detached)YesYes
restoreNoOptionalYes

Best Practices

  1. Never reset public commits - Once pushed and shared, use git revert instead
  2. Use —soft for commit amendments - Safer than git commit --amend for multiple commits
  3. Prefer restore over reset for files - git restore is clearer for file operations:
    # Old way
    git reset -- file.txt
    
    # New way (Git 2.23+)
    git restore --staged file.txt
    
  4. Check reflog when in doubt - You can almost always recover with git reflog
  5. Use —keep for safety - When removing commits, --keep prevents data loss

Common Mistakes and Solutions

Accidentally Used —hard

# Check reflog
git reflog

# Find the commit before reset
# Reset back to it
git reset --hard HEAD@{1}

Reset the Wrong Number of Commits

# Go back to any point in reflog
git reset --hard HEAD@{2}

Reset on the Wrong Branch

# Switch to the correct branch
git checkout correct-branch

# Reset to desired state
git reset --hard HEAD@{1}

# Fix the wrong branch
git checkout wrong-branch
git reset --hard <correct-commit>

Configuration

No configuration needed, but useful aliases:
[alias]
    # Undo last commit, keep changes
    undo = reset --soft HEAD~1
    
    # Unstage everything
    unstage = reset
    
    # Discard all changes (with confirmation)
    nuke = !git reset --hard HEAD && git clean -fd