Technology git reset

Learn with diagrams, code, systems and practical examples.

</>
Git · state and history recovery

git reset: move only the Git state you can name and verify

Reset changes Git state at one of two levels: it can update staged paths, or it can move HEAD to another commit and optionally make the index and working tree follow. The safest reset starts by naming exactly which state should move.

gitversion controlresetHEADindexstaging area
1 · Inspect the state

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.
2 · Predict before running

Which of HEAD, index, and working tree should change?

Think first

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.

Soft, mixed, or hard?

Think of reset modes as a state-transition table

--soft

Move HEAD. Keep index and working tree as they are.

--mixed

Move HEAD and reset the index. Keep working-tree files.

--hard

Move HEAD and make index plus tracked working-tree files match the target.

Reset, restore, or revert?

Choose by the state transition, not by the word “undo”

reset

Move HEAD or reset index state when local history/state itself needs to change.

restore

Change path content in the working tree/index without moving the branch tip.

revert

Add a corrective commit when published history should remain auditable and intact.

3 · Run deliberately

Options that decide how far reset reaches

--soft

Move HEAD to the target commit while leaving the index and working tree unchanged.

Rebuilding recent private commits while keeping their combined changes staged.
--mixed

Move 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.
--hard

Move 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.
--keep

Move 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.
--merge

Reset 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, --patch

Interactively choose hunks whose staged state should be reset.

Unstaging only part of a file rather than resetting every staged hunk in that path.
Worked examples

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.yml

The 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 status

The 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.

4 · Verify and recover

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.

  1. 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
  2. Find the prior HEAD

    Inspect recent HEAD movements and identify the entry before the mistaken reset.

    git reflog --date=local -10
  3. 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>
  4. Choose the correct repair

    Move the branch only after confirming whether you need history movement, local file restoration, or a published-history revert.

  5. 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
Common mistakes

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.
Guided practice

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.

Independent practice

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.

FAQ

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.

Next step

Use restore for path state, revert for published history, and reset for deliberate local state/history movement.

The three commands overlap just enough to be confusing. The durable skill is choosing from the state transition: restore changes local path state, revert adds corrective history, and reset can move HEAD while optionally synchronizing the index and working tree.