Recovering from Git mistakes: reflog, reset and revert
Git gives you several ways to recover from mistakes, but the right command depends on whether the change is local, shared, committed, or still only in the working tree. The safest…
Git gives you several ways to recover from mistakes, but the right command depends on whether the change is local, shared, committed, or still only in the working tree. The safest recovery starts by identifying what state you need to preserve.
Stop and inspect the state
Before running a destructive command, inspect the repository:
git status
git log --oneline --decorate --graph -n 20
git diff
git diff --stagedThese commands show the current branch, staged changes, unstaged changes, and recent commits. If there is any work you cannot lose, save it before continuing.
For a quick safety copy of uncommitted work, create a patch:
git diff > recovery.patch
git diff --staged > recovery-staged.patchA patch is not a substitute for understanding the state, but it gives you an extra recovery point before using commands that move branch tips or overwrite files.
Use reflog to find where a reference used to point
The reflog records when the tips of branches and other references were updated in the local repository. It is especially useful after a reset, rebase, merge, commit amend, or branch switch that moved HEAD.
Show the recent positions of HEAD:
git reflogYou may see entries such as a rebase, reset, commit, checkout, or merge. Each entry has a selector such as HEAD@{1} or HEAD@{2}, meaning where HEAD used to point that many moves ago. Those selectors can be used with other Git commands.
To inspect an old position before changing anything:
git show HEAD@{2}
git log --oneline --decorate -n 5 HEAD@{2}To create a recovery branch at that point:
git switch -c recovery/old-head HEAD@{2}Creating a branch is often safer than immediately resetting back. It preserves the candidate recovery point while you compare it with the current branch.
Use reset for local history changes
git reset moves the current branch tip to another commit. Depending on the mode, it may also update the index and working tree.
Use reset when the commits are local and have not been shared, or when everyone sharing the branch has agreed to rewrite it.
Undo the last commit but keep the changes staged
git reset --soft HEAD~1This moves the branch back by one commit and leaves the index and working tree unchanged, so the changes stay staged. Use it when the commit was made too early and you want to adjust the message or add more staged changes.
Undo the last commit and unstage the changes
git reset --mixed HEAD~1This is the default reset mode. It moves the branch back by one commit and updates the index to match, so nothing stays staged, while the file changes remain in the working tree. Use it when you want to split a commit or restage only part of the work.
Discard local changes and commits
git reset --hard HEAD~1This moves the branch and overwrites the index and working tree to match the target commit. It discards uncommitted changes and can remove untracked files in the affected paths. Use it only when you are certain the changes are not needed or you have saved them elsewhere.
Use revert for shared history
git revert records a new commit that reverses the effect of an earlier commit. It does not remove the original commit from history.
Use revert when the bad commit has already been pushed to a shared branch, especially the default branch. It keeps history consistent for everyone who has already fetched or based work on that commit.
To revert one commit:
git revert <commit>To revert several commits without committing each one immediately:
git revert --no-commit <oldest-commit>^..<newest-commit>
git commitThe --no-commit flag applies the reverse changes to the working tree and index without making a commit. Review the resulting diff before committing. A revert is still a code change and can conflict with later work.
Recover from an accidental hard reset
If you ran git reset --hard and moved away from commits you still need, use the reflog.
First, find the old position:
git reflogThen create a recovery branch:
git switch -c recovery/before-reset HEAD@{1}Inspect the branch. If it is correct, either keep it as the restored work branch or move the original branch back intentionally:
git switch main
git reset --hard recovery/before-resetOnly do the final hard reset if that branch is local or rewriting it is safe for the team.
Recover from a bad rebase
If a rebase produced the wrong result, stop it while it is still running:
git rebase --abortIf the rebase has already completed, use the reflog to find the pre-rebase position:
git reflog
git switch -c recovery/before-rebase HEAD@{3}Compare the recovered branch with the current branch before deciding what to keep.
Recover a deleted branch
A deleted branch name can often be recreated if the commit it pointed to is still available in the reflog or another reference.
Search the reflog for the branch tip or the work you recognise:
git reflog --allThen recreate the branch:
git switch -c feature/restored <commit>Reflog retention is not permanent. Reachable entries expire after 90 days by default and unreachable entries after 30 days, controlled by gc.reflogExpire and gc.reflogExpireUnreachable. Do not treat the reflog as a backup.
Choose the command by the risk
Use this rule of thumb:
- use
restoreor checkout style operations for individual files - use
resetfor local commits that can be rewritten - use
reflogto find previous reference positions - use
revertfor changes already shared with others - create a recovery branch before making another destructive move
The dangerous part is not the command name. The dangerous part is changing history that other people already have, or overwriting working tree changes that were never saved.
Conclusion
Git recovery is manageable when you slow down and inspect the state first. Reflog helps you find where HEAD and branches used to point. Reset is for local history you can safely rewrite. Revert is for shared history that must remain consistent. When in doubt, create a recovery branch before making the next change.
