git reset
git reset moves HEAD (and optionally the index / working tree) to a chosen commit. Three modes: --soft, --mixed (default), --hard. The most-feared git command — with reflog, it’s recoverable.
Soft, mixed, hard, recovery
EXAMPLE
# 1) Three modes — same operation, different scope
#
# HEAD = pointer to current commit
# Index (staging) = what 'git commit' would record
# Working tree = your actual files
#
# --soft : moves HEAD only; index + working tree untouched
# --mixed : moves HEAD + index; working tree untouched (DEFAULT)
# --hard : moves HEAD + index + working tree (DESTRUCTIVE)
# 2) Undo last commit, KEEP changes staged
git reset --soft HEAD~1
# Use case: you committed too soon; want to amend / add more
# 3) Undo last commit, KEEP changes UNSTAGED
git reset HEAD~1
# Or: git reset --mixed HEAD~1
# Use case: split a big commit into smaller ones; re-stage selectively
# 4) Undo last commit, THROW AWAY changes (DESTRUCTIVE)
git reset --hard HEAD~1
# Use case: committed broken code; want to start over
# Recovery: git reflog → find the old SHA → git reset --hard <SHA>
# 5) Reset to a specific commit
git reset --hard abc1234
git reset --hard origin/main # match the remote exactly
# 6) Unstage specific files (without changing them)
git reset HEAD path/to/file.ts
git reset HEAD . # unstage everything
# Modern alias: git restore --staged <file>
# 7) Reset a file to a specific commit's state
git checkout abc1234 -- path/to/file.ts
# Or:
git restore --source=abc1234 path/to/file.ts
# === Reset vs revert vs checkout ===
# reset : moves HEAD (rewrites history) — DESTRUCTIVE on shared branches
# revert : creates a NEW commit that undoes a previous one — safe to push
# checkout : changes working tree to match a commit (without moving HEAD)
# When to use which:
# - PRIVATE branch, want to redo: reset
# - PUBLIC / shared branch: revert
# - Just look at old state: checkout
# 8) Revert (safe alternative to hard reset on shared branches)
git revert abc1234
# Creates a new commit on top of HEAD that undoes abc1234.
# Safe to push; doesn't rewrite history.
git revert HEAD~3..HEAD # revert last 3 commits
git revert --no-commit HEAD~3..HEAD # group all into one new commit
git commit -m 'Revert features X, Y, Z'
# 9) Reflog — the safety net
git reflog
# 0a8c5d1 HEAD@{0}: reset: moving to HEAD~3
# 1b2c3d4 HEAD@{1}: commit: WIP feature
# 5e6f7g8 HEAD@{2}: commit: fix bug
# ...
# Recover from a 'lost' commit:
git reset --hard HEAD@{2} # jump back to commit 5e6f7g8
# OR
git checkout 5e6f7g8 # detached HEAD; cherry-pick what you need
# Reflog keeps entries for ~30 days by default.
# After that, git gc may collect them — usually still recoverable for a while.
# 10) Recover deleted files (if not committed)
# If you `git reset --hard` lost uncommitted work, it MIGHT be in 'git fsck':
git fsck --lost-found
ls .git/lost-found/other/ # dangling blobs
# Open each blob to see if it's your file.
# 11) Reset for branch alignment
# 'I want my branch to match origin/main exactly':
git fetch origin
git reset --hard origin/main
# This is a 'I give up; sync to upstream' move on a local branch.
# 12) Reset selectively — split a commit
git reset --soft HEAD~1 # uncommit, keep changes staged
git reset HEAD path/to/file1 # unstage file1 specifically
git commit -m 'just file2 + file3 change'
git add path/to/file1
git commit -m 'file1 change'
# Or use git rebase -i for interactive history editing.
# 13) Hard reset on a remote branch — DANGEROUS
# This requires --force-with-lease (or --force):
git reset --hard <SHA>
git push --force-with-lease # refuses if remote moved
# Rules:
# ✅ OK on private feature branches you own
# ❌ NEVER on shared branches (main, develop, release/*)
# ❌ NEVER on tagged commits
# 14) Common bugs
# • `git reset --hard` on the wrong commit → lost changes
# Recovery: git reflog → reset to the right SHA
# • Used reset on a shared branch → teammates' next push rejected
# Recovery: hard reset back to where it was, force-push
# • Confusing reset with checkout — checkout HEAD~3 detaches HEAD; doesn't move it
# • `--mixed` (default) leaves stuff in working tree that you might not realise → confused diffs
# 15) Modern aliases (since git 2.23)
git restore <file> # discard changes in working tree
git restore --staged <file> # unstage
git restore --source=<commit> <file> # restore from a specific commit
git switch <branch> # change branch (replaces checkout <branch>)
git switch -c <newbranch> # create + switch
# These commands have CLEARER semantics than the overloaded `git reset` / `git checkout`.
# 16) Decision tree
#
# 'I want to undo my last commit BUT keep the changes':
# - staged? → git reset --soft HEAD~1
# - unstaged? → git reset HEAD~1
# - delete? → git reset --hard HEAD~1 (only if you don't need the changes)
#
# 'I pushed a bad commit to a shared branch':
# - git revert <bad SHA>
# - git push
#
# 'I lost work after a bad reset':
# - git reflog
# - git reset --hard HEAD@{N}
#
# 'I want my local branch to match the remote exactly':
# - git fetch && git reset --hard origin/<branch>
#
# 'I want to discard ALL local changes':
# - git reset --hard HEAD
# - git clean -fd (also remove untracked files + dirs)
Why it matters
The golden rule: --hard is recoverable via git reflog. --soft keeps your work staged; --mixed (default) leaves it in the working tree. Reach for git revert on shared branches — never reset + force-push there.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
git reset --soft HEAD~1 # uncommit, keep changes staged git reset --mixed HEAD~1 # uncommit + unstage git reset --hard HEAD~1 # destroy! (and the work)Try it Yourself »
Exercise
Uncommit but keep changes staged.
git reset
HEAD~1
Six characters; starts with --.
Discussion
Loading…