Separate branch history, index state, and working-tree edits
- Run git status and inspect both git diff and git diff --staged so uncommitted work is visible.
- Inspect git log --graph --oneline --decorate --all and identify the exact commit/reference the branch should point to afterward.
- Confirm whether the commits being moved are private; shared published history usually calls for revert instead.
- Choose the least destructive mode that achieves the intended transition: path reset, --soft, --mixed, --keep, or only then --hard when discard is deliberate.
Which of HEAD, index, and working tree should change?
HEAD points to commit C. Commit C introduced app.js changes. You run git reset --soft HEAD~1. Where do those app.js changes go?
Reveal the reasoning
HEAD moves back to the parent commit, but the index and working tree stay unchanged. The changes introduced by C therefore remain staged, ready to be recommitted or reorganized.
Think of reset modes as a state-transition table
Move HEAD. Keep index and working tree as they are.
Move HEAD and reset the index. Keep working-tree files.
Move HEAD and make index plus tracked working-tree files match the target.
Choose by the state transition, not by the word “undo”
Move HEAD or reset index state when local history/state itself needs to change.
Change path content in the working tree/index without moving the branch tip.
Add a corrective commit when published history should remain auditable and intact.
Options that decide how far reset reaches
--softMove HEAD to the target commit while leaving the index and working tree unchanged.
Rebuilding recent private commits while keeping their combined changes staged.--mixedMove HEAD and reset the index to the target while leaving working-tree files unchanged. This is the default mode.
Undoing a recent private commit while keeping its file changes available but unstaged.--hardMove HEAD and make both the index and tracked working-tree files match the target commit.
Only after proving that the commits and local tracked-file changes being discarded are intentionally disposable.--keepMove HEAD and update files where possible, but abort when local working-tree changes would be overwritten.
Removing recent private commits while attempting to preserve unrelated local edits.--mergeReset the index and update working-tree files while carrying forward certain unmerged/local changes.
Specialized recovery after a conflicted merge-like operation when the repository state matches that mode’s constraints.-p, --patchInteractively choose hunks whose staged state should be reset.
Unstaging only part of a file rather than resetting every staged hunk in that path.Practice from reversible to destructive
Beginner: unstage one file without losing the edit
You staged config/app.yml too early but still want every working-tree edit to remain.
git status
git diff --staged -- config/app.yml
git reset HEAD -- config/app.ymlThe path form updates the index entry from HEAD. It does not move the branch tip and does not replace the working-tree file. Modern Git also offers git restore --staged for this intent.
Practical: undo the last private commit but keep changes staged
Your latest local commit has the wrong boundaries. Nobody else has pulled it, and you want to rebuild the commit while keeping its changes staged.
git log --oneline -3
git reset --soft HEAD~1
git status--soft moves HEAD back one commit but leaves index and working tree unchanged. The changes from the removed commit therefore remain staged for a replacement commit.
Practical: undo the last private commit and keep changes unstaged
You want the changes from the latest local commit back in your working tree so you can edit and regroup them before recommitting.
git log --oneline -3
git reset --mixed HEAD~1
git status
git diff--mixed moves HEAD and resets the index to the new HEAD, while leaving the working-tree content in place. The former commit changes become unstaged edits.
Advanced: inspect before a destructive hard reset
A disposable experiment branch contains two unwanted commits and local tracked-file edits. You intend to make it exactly match origin/main.
git status
git log --graph --oneline --decorate --all -8
git diff
git fetch origin
git reset --hard origin/main
git statusThe inspection is part of the command. --hard changes HEAD, index, and tracked working-tree content. Use it only when the local commits and edits are intentionally disposable and the target reference is the exact state you meant to adopt.
If reset moved history unexpectedly, stop and inspect the reflog before doing more
Git records recent HEAD movements in the reflog, which often makes a mistaken branch-tip reset recoverable. That is different from guaranteeing recovery of uncommitted file content overwritten by --hard.
- Read the current state
Do not immediately run another reset. Capture the branch, working-tree, and index state first.
git status git log --graph --oneline --decorate -8 - Find the prior HEAD
Inspect recent HEAD movements and identify the entry before the mistaken reset.
git reflog --date=local -10 - Protect important work
If the old commit is visible, create a temporary branch or tag before experimenting further so that commit remains easy to reach.
git branch recovery-before-reset <old-sha> - Choose the correct repair
Move the branch only after confirming whether you need history movement, local file restoration, or a published-history revert.
- Verify before pushing
Inspect the recovered graph and run relevant tests before any force-push or collaboration-affecting action.
git log --graph --oneline --decorate --all git status
Failure modes worth recognizing before they cost work
Using --hard as a generic “make Git clean” button
--hard can discard tracked working-tree changes and move the branch tip. A clean status afterward does not prove the discarded work was unimportant.
Recovery: Inspect status, diffs, graph, and target first. If a reset already happened, check git reflog promptly for the prior HEAD; uncommitted overwritten file content may be harder or impossible to recover.Resetting published commits that teammates already use
Moving a shared branch backward replaces the branch tip people expect. A force-push can then make collaborators reconcile rewritten history.
Recovery: For already-published history, prefer an auditable corrective commit with git revert unless the team has explicitly agreed to history rewriting.Confusing path reset with commit reset
git reset HEAD -- file updates the index for that path and does not move HEAD; git reset --mixed HEAD~1 moves HEAD and resets the index for the whole commit state.
Recovery: State whether the goal is “unstage this path” or “move this branch tip” before choosing the command form.Assuming reset and restore are interchangeable
The tools overlap for index/path operations, but reset is also a history-moving command. Restore is intentionally focused on working-tree and index content without moving the branch tip.
Recovery: For local path state, prefer restore when it expresses the intent clearly. Use reset when changing HEAD or when a reset-specific index operation is deliberate.Predict which state must move before revealing the command
You made one local commit too early. Nobody else has it. You want the exact file changes back as unstaged edits so you can split them into two commits. Which reset mode fits?
Hint
You need HEAD to move back, the index to match the new HEAD, and the working-tree changes to stay.
Reveal command and reasoning
git log --oneline -3
git reset --mixed HEAD~1
git status
git diff--mixed moves HEAD and resets the index while preserving working-tree content. That turns the old commit patch into unstaged edits without discarding it.
Build a reset matrix you can explain from memory
In a disposable repository, create two commits and one extra unstaged edit. Record git status, git diff, git diff --staged, and git log. Test --soft and --mixed on separate disposable copies or by restoring the setup through reflog, then compare exactly which of HEAD, index, and working tree changed. Finish by creating throwaway work and practicing one --hard reset only after predicting the losses.
Questions that change the recovery choice
What is the default reset mode?
For commit-form reset, the default is --mixed: HEAD moves to the target, the index is reset to match that target, and working-tree files are left unchanged.
What is the difference between --soft, --mixed, and --hard?
--soft moves HEAD only. --mixed moves HEAD and resets the index. --hard moves HEAD and makes both index and tracked working-tree files match the target.
Can reset unstage a file without moving HEAD?
Yes. The path form, such as git reset HEAD -- file, resets the index entry for that path while leaving the current branch tip and working-tree file alone. git restore --staged is the clearer modern alternative for many users.
How is reset different from revert?
Reset can move a branch tip and is best suited to local/private history changes. Revert preserves published history by adding a new commit that applies the inverse patch of an earlier commit.
Can I recover after moving HEAD with reset?
Often the previous branch tip can still be located in git reflog, especially if you notice quickly. That does not guarantee recovery of uncommitted content overwritten by a hard reset, which is why pre-reset inspection matters.