Git Reset
Change the last unpublished commit, restore files, and use mixed, soft, and hard reset only when you can name the risk.
Name the state before choosing the command
Ask three questions before correcting history
SCOPEWhich state is wrong?The latest commit, the staged proposal, an unstaged file, or the current branch tip require different operations.
VALUEWhich content must survive?Name the exact committed, staged, unstaged, and untracked work you intend to keep.
SHARINGHas the commit been published?Rewriting a shared commit changes its ID and can break another person’s ancestry.
EVIDENCECan you prove the current state?Capture status, relevant diffs, branch name, and commit IDs before changing pointers or files.
Amend the latest unpublished commit
git commit --amend does not edit a commit in place. It creates a new commit, normally with the same parent as HEAD, then moves the current branch to the replacement. The old and new commits have different object IDs even if only the message changes.
# Inspect before changing the latest commit
git status
git diff
git diff --cached
git show --stat HEAD
# Stage the intended correction, then replace HEAD
git add path/to/file
git commit --amend
# Keep the existing message when only the snapshot changes
git commit --amend --no-editStage the intended content
The replacement commit uses the current index. Review git diff --cached immediately before amending.
Open the commit editor
Run amend without --no-edit, correct the subject or body, save, and verify the new commit with git show.
Test what you learned
Type the command that replaces HEAD using the staged snapshot while retaining the existing message.
Use restore for file-level state changes
git restore copies content from a source into one or more destinations. By default, the destination is the working tree and the source is the index. With --staged, the destination includes the index and the default source becomes HEAD.
# Discard unstaged changes in one tracked path:
git restore -- path/to/file
# Unstage a path while keeping its working copy:
git restore --staged -- path/to/file
# Restore a path from a named commit into the working tree:
git restore --source=HEAD~1 -- path/to/file
# Update both index and working tree from a named commit:
git restore --source=HEAD~1 --staged --worktree -- path/to/fileDiscard unstaged edits
git restore -- file makes the tracked working file match the index. Staged content remains staged.
Unstage but keep edits
git restore --staged -- file resets the index copy from HEAD while preserving the working file.
Recover historical content
--source=REV names the commit or tree to copy from. Review the resulting diff before staging it.
Update both destinations
Combining --staged --worktree overwrites both copies. This is broader and needs explicit evidence.
Choose restore by scenario
Keep the work, change the proposal
Run git restore --staged -- path. Confirm the file moves from the index column to the working-tree column in short status.
Discard only the working change
Inspect git diff -- path, then run git restore -- path only if every displayed line is disposable.
Bring history forward as a new change
Restore the path from a named revision into the working tree, inspect the diff, then commit that result normally.
Restore is not the cleanup tool
An untracked path has no indexed copy to restore. Review it manually; do not reach for broad cleanup commands as a shortcut.
Test what you learned
Type the command that unstages config.json while preserving its working content.
Reset moves the current branch
At commit level, git reset TARGET moves the current branch tip to TARGET. The mode controls whether Git also resets the index and tracked working-tree content. This changes which commits the branch reaches, so inspect the graph and copy the current tip ID first.
# Move the current branch; keep index and working tree unchanged
git reset --soft HEAD~1
# Move the current branch; reset index; keep working files
git reset --mixed HEAD~1
# Move the current branch; reset index and tracked working files
git reset --hard HEAD~1--soft
Move branch: yes. Reset index: no. Reset working tree: no. Former commit content remains staged.
--mixed
Move branch: yes. Reset index: yes. Reset working tree: no. This is the default commit-level mode.
--hard
Move branch: yes. Reset index: yes. Reset tracked working tree: yes. Uncommitted tracked changes can be lost.
Path reset differs
git reset HEAD -- path updates the index for a path but does not move the branch. Prefer restore for clearer unstaging intent.
Predict the result before reset
Soft reset
- Use for a local commit assembled at the wrong boundary.
- The branch moves back.
- The index still contains the former snapshot.
- Review before recommitting.
Mixed reset
- Use to reassemble one or more local commits.
- The branch moves back.
- The index matches the target.
- Working edits remain on disk.
Build a recovery point first
git status --short
git diff
git diff --cached
git log --oneline --decorate -5
git rev-parse HEAD
# Give the current commit a temporary recovery name when appropriate:
git branch recovery/before-reset HEAD
# Verify the ref before changing the original branch:
git show --stat recovery/before-resetA temporary branch protects committed history by keeping the old commit reachable. It does not preserve unstaged or untracked content. Save or commit valuable uncommitted work using an approved workflow before an operation that could overwrite it.
Recover a moved branch tip with reflog
The local reflog records recent updates to refs such as HEAD. If reset moved a branch away from a committed tip, inspect the reflog, identify the exact prior commit, and create a recovery branch before doing more work.
git reflog --date=iso
git show --stat HEAD@{1}
# After verifying the object:
git branch recovery/lost-tip HEAD@{1}
git log --oneline --decorate --graph --allReflog is local, temporary recovery metadata—not a substitute for a shared remote or a preservation plan. Entries expire, differ between clones, and cannot reconstruct arbitrary working-tree text that Git never stored.
Guided practice: correct without losing work
Create a disposable repository with a README and two source files. Make at least three small commits so every pointer movement is visible.
- 01Amend locally
Make a fourth commit with an incomplete README, stage the correction, amend with the same message, and prove the object ID changed.
- 02Separate staged state
Edit two files, stage both, then unstage one with restore while keeping both working copies.
- 03Discard one known edit
Inspect the unstaged patch for one disposable line and restore only that path.
- 04Compare reset modes
Create a recovery branch, then use soft and mixed reset in turn. Record branch, index, and working-tree results.
- 05Recover the tip
Locate the prior committed tip in reflog, create a recovery branch there, and draw the complete graph.
Independent lab: repair a mistaken local commit
In a disposable repository, create a clean three-commit history. Then create a fourth commit that combines one intended source edit, one unrelated documentation edit, and an inaccurate message. Without deleting any wanted content:
- Record status, both diffs, the graph, and the original HEAD ID.
- Create and verify a recovery branch at the original tip.
- Use mixed reset to move the original branch back one commit while retaining working files.
- Stage and commit the source change with an accurate message.
- Stage and commit the documentation change separately.
- Add an unstaged disposable edit, prove its exact patch, and restore only that path.
- Use reflog and the recovery branch to explain where the original combined commit remains reachable.
- Finish with clean status and a graph showing the repaired branch and recovery pointer.
Common correction mistakes
Amending after push
The replacement commit has a new ID. Prefer a follow-up commit when history may already be shared.
Restoring the wrong source
Defaults change with the destination. Name --source when historical content is intended.
Using hard reset reflexively
It overwrites tracked state and may destroy uncommitted content that no Git object can recover.
Assuming reflog saves everything
It records ref movement, not every version ever typed into a working file.
Ignoring untracked files
Reset modes focus on tracked state. Status and a separate preservation decision are still required.
Skipping post-operation proof
Re-run status, diffs, show, and log to verify the result instead of trusting silence.
Lesson review
You can now correct unpublished history without treating Git’s recovery commands as interchangeable. You can replace the latest local commit, move file content between states, predict each reset mode, preserve a committed recovery point, and use reflog without overpromising what it stores.
- I know amend creates a replacement commit with a new object ID.
- I distinguish discarding an unstaged edit from unstaging content I want to keep.
- I can predict branch, index, and working-tree state after soft, mixed, and hard reset.
- I inspect and preserve valuable work before moving pointers or overwriting files.
- I reserve history rewriting for unpublished work or an explicitly coordinated workflow.