Soft reset undoes a commit but keeps the changes. This lesson is
about the other end of the spectrum: git reset --hard, which
undoes a commit and throws away every change that was in it.
Hard reset is Git's most destructive routine command. Used appropriately, it's how you say "throw this local experiment away and pretend it never happened." Used carelessly, it deletes your work with no warning.
The command
git reset --hard HEAD~1
Three things happen at once:
- HEAD moves back by one commit.
- The staging area is reset to match that older commit.
- The working tree is reset to match that older commit too.
Files revert on disk to whatever the older commit had. Any changes that were in the removed commit are gone from your files. Any uncommitted changes are also gone. Everything after the target commit is erased from your working state.
When hard reset is the right tool
- You made a local commit as an experiment, tested it, and decided it was wrong. You want to abandon it completely.
- You're on a branch that only you touch (or a personal fork), and you want to rewind to an earlier state.
- You're using Git as a rollback tool while trying different approaches to a problem, committing between each and hard resetting to the last known-good spot when something breaks.
The common thread: local work, undoable, no one else affected.
Recovery is possible for commits, not for uncommitted work
Because hard reset moves HEAD, git reflog still knows where you
were before the reset. If you regret it, you can usually get
back:
git reflog
# find the commit you meant to keep
git reset --hard <sha-from-reflog>
Reflog entries survive for about 30 days. This is why "hard reset away a commit" is not truly irreversible in the short term.
Uncommitted changes, though, are gone the moment you run hard reset. Reflog only remembers commits. If you had modified files on disk that were never committed, hard reset deletes them and nothing brings them back. This is the one place Git will not save you.
Before you hit enter, check for uncommitted work
Before every git reset --hard, run git status. If you see any
modified files or staged changes you might want to keep, deal
with them first:
- Commit them (even a throwaway WIP commit is enough; reflog can bring the commit back).
- Stash them with
git stash push -m "reason". - Copy them elsewhere on disk if you're paranoid.
Then hard reset. This one habit prevents ~95% of the "I lost my work" stories in Git.
Reset vs revert on shared branches
Hard reset on a branch you've pushed doesn't just undo locally. The remote still has the commits. If you push after the reset, Git refuses because your local history no longer matches the remote's. If you force-push to "fix" that, you've now erased commits from the remote, and anyone who pulled the old versions has ghost commits in their clone.
This is exactly the situation where the next lesson's git revert is the correct tool instead. Revert leaves history alone
and adds a new commit that undoes the effect. That's shareable.
Hard reset is not.
The nuclear one-liner
Occasionally you'll see:
git reset --hard origin/main
This means "throw away everything on my local branch and match the remote exactly." Genuinely useful when your local branch is completely broken and you want a clean slate. Every uncommitted change on your machine is destroyed. Use with intention.