git reset — Rewinding History
git reset moves the branch pointer to an earlier commit, effectively removing commits from the tip of the current branch. It is a history-rewriting operation — the commits you reset past become unreachable — so it is only appropriate for local commits that have not been shared.
Used correctly, git reset is one of the most useful tools in Git. Used on pushed commits, it causes serious problems for collaborators.
What git reset does
Section titled “What git reset does”When you run git reset <commit>, Git moves the current branch pointer (HEAD) to the specified commit. Commits that were ahead of that point are no longer reachable through the branch.
The three modes control what happens to the changes from those removed commits:
| Mode | Branch pointer | Staging area | Working tree |
|---|---|---|---|
--soft | Moved back | Keeps changes staged | Unchanged |
--mixed (default) | Moved back | Clears staged changes | Unchanged |
--hard | Moved back | Clears staged changes | Discards changes |
—soft: uncommit, keep staged
Section titled “—soft: uncommit, keep staged”git reset --soft HEAD~1Moves the branch pointer back one commit. The changes from that commit remain staged. Your files are unchanged. Effectively “uncommits” the last commit — putting everything back in the staging area as if you had staged it but not yet committed.
Use case: you committed too early and want to add more to the commit, or you want to rewrite the commit message more thoroughly.
—mixed: uncommit and unstage (the default)
Section titled “—mixed: uncommit and unstage (the default)”git reset HEAD~1# or explicitly:git reset --mixed HEAD~1Moves the branch pointer back one commit. The changes from that commit are removed from the staging area but kept in your working tree as modified (unstaged) files.
Use case: you want to undo the last commit and the staging, but keep all the changes — starting fresh with what to commit and how.
—hard: uncommit, unstage, and discard
Section titled “—hard: uncommit, unstage, and discard”git reset --hard HEAD~1Moves the branch pointer back one commit. The changes from that commit are removed from the staging area and discarded from the working tree. Destructive — the changes are gone.
Use case: you want to completely discard the last commit and return to a known clean state. The changes from that commit are lost.
Resetting to a specific commit
Section titled “Resetting to a specific commit”git reset --hard a3f8c12Moves the branch pointer to a3f8c12. All commits after that point are removed from the branch, and with --hard, all their changes are discarded.
Finding commits you have reset past
Section titled “Finding commits you have reset past”If you reset too far and want to go back, the commits still exist in Git’s object store for a while. Find them with:
git refloggit reflog is a log of everywhere HEAD has pointed. It shows resets, merges, branch switches — all movement. To recover a reset commit:
git reset --hard <hash-from-reflog>git reflog is your emergency safety net after a --hard reset you immediately regret. It keeps entries for about 90 days.
The danger: never reset pushed commits
Section titled “The danger: never reset pushed commits”If you reset past a commit that has already been pushed to a shared remote, your local history and the remote’s history diverge. Pushing requires git push --force, which overwrites the remote’s history and leaves any collaborators who pulled the old commits in a broken state.
The rule: reset is for local-only commits. For shared history, use git revert.
Practical use cases for reset
Section titled “Practical use cases for reset”Squash multiple commits into one:
git reset --soft HEAD~3 # uncommit 3 commits, keep all changes stagedgit commit -m "Combined commit message"Undo a staged git add:
git reset HEAD pine-ridge.txt# equivalent to: git restore --staged pine-ridge.txtThis is the older form of unstaging — git reset HEAD <file> moves the file out of the staging area. git restore --staged is the modern equivalent.
Exercise
Section titled “Exercise”- In your
git-practicefolder, make two commits that you want to undo locally:
echo "Draft note A" >> pine-ridge.txt && git add . && git commit -m "Draft A"echo "Draft note B" >> pine-ridge.txt && git add . && git commit -m "Draft B"- Undo both commits with
--softand inspect what happened:
git reset --soft HEAD~2git statusConfirm the changes are staged. The commits are gone from git log, but the content is still in the staging area.
- Now try
--mixed:
git reset --mixed HEAD~1 # (or just: git reset HEAD~1)git statusConfirm the changes are in the working tree but not staged.
- Finally, try
--hardon a single throwaway commit:
echo "throwaway" >> pine-ridge.txt && git add . && git commit -m "Throwaway"git reset --hard HEAD~1Open pine-ridge.txt and confirm the throwaway line is gone.
- Run
git reflogand find the commit that was just reset away.
git resetmoves the branch pointer to an earlier commit, removing later commits from the branch.--soft: moves pointer, keeps changes staged.--mixed(default): moves pointer, keeps changes in working tree (unstaged).--hard: moves pointer, discards all changes. Destructive.git reflogshows all HEAD movement and is the recovery tool after an accidental hard reset.- Never reset commits that have been pushed to a shared repository. Use
git revertfor shared history.
The next lesson is the Module 04 recap — a consolidated reference for every undo operation, when to use each, and a preview of Module 05 on working with GitHub.