HEAD is now at <commit> — uncommitted changes permanently lost after git reset --hard
ID: git/reset-hard-data-loss
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 2 | active | — | — | — |
Root Cause
git reset --hard discards ALL uncommitted changes (staged and unstaged) permanently. Unlike committed work which can be recovered via reflog, uncommitted changes have no reflog entry. Staged changes may be partially recoverable from git's object database if gc hasn't run, but unstaged changes are gone forever.
genericWorkarounds
-
98% success Use git stash before any destructive operation
Run 'git stash -u' (includes untracked files) before doing git reset --hard. This saves all working directory changes. You can recover them later with 'git stash pop' or 'git stash apply'. Make this a habit before any reset.
Sources: https://git-scm.com/docs/git-stash
-
55% success Recover staged files from git's object database using git fsck --lost-found
Run 'git fsck --lost-found' to find dangling blobs (staged files that were added but never committed). Check .git/lost-found/other/ for recovered file contents. This only works for files that were staged ('git add'), and only before 'git gc' runs. Identify files by content since filenames are lost.Sources: https://git-scm.com/docs/git-fsck
-
95% success Use git reset --soft or --mixed instead of --hard
Use 'git reset --soft HEAD~1' to undo the last commit but keep all changes staged. Use 'git reset --mixed HEAD~1' (or just 'git reset HEAD~1') to undo the commit and unstage changes but keep them in working directory. Only use --hard when you truly want to discard everything.
Sources: https://git-scm.com/docs/git-reset
-
97% success Recover committed work (not uncommitted) via git reflog
Run 'git reflog' to find the commit hash before the reset. Then 'git reset --hard <hash>' or 'git checkout -b recovery <hash>' to restore. Reflog entries expire after 90 days by default. This only recovers committed code, not uncommitted working directory changes.
Sources: https://git-scm.com/docs/git-reflog
Dead Ends
Common approaches that don't work:
-
Looking in git reflog for uncommitted changes that were lost
95% fail
Reflog only tracks changes to HEAD (commits, checkouts, rebases). Uncommitted working directory changes and staged-but-not-committed changes are NOT recorded in the reflog. git reflog will show the commit you reset TO, but the uncommitted work that was discarded is not recoverable this way.
-
Running git reset --hard HEAD~1 expecting it to be easily reversible like git revert
85% fail
git reset --hard moves the branch pointer AND overwrites the working tree. Unlike git revert (which creates a new commit to undo changes), reset --hard is destructive. The commit itself IS recoverable via reflog, but any uncommitted modifications in the working directory at the time of the reset are permanently lost.
-
Using git checkout -- . or git restore . thinking it's safer than reset --hard
90% fail
Both git checkout -- . and git restore . also permanently discard unstaged changes in the working directory. They don't touch staged changes (unlike reset --hard which also destroys staged work), but the effect on unstaged modifications is equally destructive and equally unrecoverable.