Undoing Changes, Reset, Restore, Revert, Reflog, and Lost-Work Recovery: Diagnostics, Failure Modes, Security, and Performance
Diagnose recovery failures caused by wrong-layer hard reset, wrong-source restore, clone-local reflog assumptions, unsafe clean, merge-revert ambiguity, and premature garbage collection.
Learning objectives
- Apply a preserve-evidence-first recovery diagnostic sequence.
- Replace broad destructive responses with path/layer-specific corrections.
- Explain why reflog evidence does not automatically exist in another clone/server.
- Diagnose merge-revert mainline requirements and fsck object-state messages.
- Prevent cleanup and aggressive maintenance from destroying recovery options.
1. Diagnostic sequence for recovery incidents
- Preserve evidence: save exact OIDs, status/diff output, reflog, remote refs, and any deployment/review identifiers.
- Inspect status, refs, history, and config.
- Identify the affected layer: working tree, index, current ref/history, shared history, untracked files, or object retention.
- Choose the least destructive correction.
- Verify both content and graph, then document what moved.
2. Failure mode — using hard reset when only unstaging was needed
Symptom: a developer stages config.yml by mistake and
thinks “reset it.” A hard reset would also overwrite tracked
working-tree content.
git status --short
git diff --staged -- config.yml
git diff -- config.yml
If only the index is wrong, the narrow correction is:
git restore --staged -- config.yml
git status --short
git diff -- config.yml
git reset --hard.
Recovery scope should match failure scope.
3. Failure mode — restoring from the wrong source revision
git restore --source=HEAD~2 -- app.conf intentionally
copies that old tree's file into the working tree. It does not mean
“undo two edits” or “find last good.”
git show HEAD~2:app.conf
git diff HEAD~2 -- app.conf
git status --short
If the wrong source was restored but not staged/committed, the
current index may still contain the pre-restore version; inspect
git show :app.conf, then restore from the index if that
is the intended state.
4. Failure mode — expecting another clone's reflog to exist here
A teammate says “my branch was at commit X yesterday; check the
reflog on the server.” Core Git reflogs are repository-local. Your
clone's HEAD@{1} describes your ref movements,
not theirs.
git reflog --all
git remote -v
git ls-remote origin
git log --all --decorate --oneline --max-count=30
Search refs/remote state and ask the teammate to inspect their own reflog. A hosting service may have separate administrative recovery capabilities, but those are platform-specific and not guaranteed by core Git.
5. Failure mode — running clean without dry-run
git status --short
git clean -n
git clean -nd
git clean -nx
Only after confirming every candidate in a disposable lab should you
demonstrate deletion with a narrow pathspec. Avoid
-x unless the explicit goal is to include ignored
artifacts; avoid -fdx as a routine cleanup pattern.
6. Intentionally broken example — reverting a merge without mainline
A merge commit has more than one parent. Running:
git revert <merge-commit>
typically fails with an error explaining that the commit is a merge
and no -m option was given. This is valuable: Git
refuses to guess which parent represents the mainline.
git show --no-patch --format='%H %P %s' <merge-commit>
git rev-list --parents -n 1 <merge-commit>
If parent 1 is the mainline you intend to preserve, the deliberate
form is git revert -m 1 <merge-commit>. But
understand the semantic consequence: reverting a merge declares that
the tree changes introduced by that merge are unwanted; later merge
behavior can therefore differ from “pretend the merge never
happened.”
7. Failure mode — aggressive maintenance before recovery
If you are searching for reset-away or deleted-branch objects, do not expire reflogs or prune unreachable objects first.
git reflog expire --expire=now --all,
git prune --expire=now, and
git gc --prune=now can reduce or destroy recovery
options. Preserve evidence and create safety refs before
maintenance.
git reflog --all
git fsck --unreachable
git fsck --no-reflogs --unreachable
The difference between the last two commands can reveal objects protected only through reflog roots.
8. Read fsck diagnostics precisely
| Diagnostic | Meaning |
|---|---|
unreachable commit |
Object exists but no selected root reaches it |
dangling commit |
Commit exists but is not directly used in the traversed graph |
missing blob/tree/commit |
A referenced object is absent; this is corruption, not merely lost reachability |
hash mismatch |
Serious object-integrity problem |
A missing/corrupt object must generally come from another intact copy, backup, or archive; reflog cannot recreate bytes that no longer exist.
9. Security implications tied to recovery
- Do not expose private repository contents by uploading a full bundle to an untrusted location merely because you are under incident pressure.
- A recovered secret-bearing commit remains sensitive; rotate/revoke leaked credentials first.
- Do not disable server protections or use force push merely to “make history match” during recovery.
- Preserve forensic timestamps/OIDs before destructive repair if an incident investigation is underway.
10. Performance implications that matter during recovery
git fsck and full object traversal can be expensive in
large repositories. Start with narrow evidence—known refs, reflogs,
exact OIDs, git show—and escalate to broader
connectivity checks when needed. Likewise, do not run aggressive GC
merely because the repository feels slow while recovery is
unresolved; performance optimization and evidence preservation have
different priorities.
11. Symptom → evidence → narrow response
| Symptom | First evidence | Narrow response |
|---|---|---|
| Wrong file staged | staged/unstaged diff | restore --staged path |
| Wrong historical file restored | index + source commit content | Restore from correct source/index |
| Branch deleted | HEAD/all reflog + known OID | Create recovery branch |
| Published bad commit | remote refs + graph | Revert |
| Untracked clutter | clean -n |
Narrow forced clean only if every candidate is disposable |
| Potential lost object | reflog then fsck | Create safety ref before maintenance |
12. Knowledge check
Question 1. Why is hard reset an incorrect response to accidental staging?
Question 2. Why can't you rely on your local reflog to recover a teammate's deleted local branch?
Question 3. Why does reverting a merge require a mainline parent?
Question 4. Why is clean -n more than a
convenience?
Question 5. What is the difference between an unreachable object and a missing object?
13. Summary
The dangerous recovery shortcuts all share one mistake: changing more state than the diagnosis justifies. Preserve evidence, identify the exact layer and source, choose the smallest correction, and delay cleanup/maintenance until recovery is complete.
Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.