Undo Old Commits โ revert vs reset
Shared branch pe revert karo, local branch pe reset. Yeh golden rule yaad rakhna โ team ka history mat todo!
git revert is the safe way to undo a commit. It creates a NEW commit that applies the inverse of the original changes โ like adding a negative to cancel out a positive.
The original commit stays in history, completely untouched. Nothing is deleted, nothing is rewritten. The revert commit simply says "undo these specific changes."
This is why revert is the only safe way to undo commits on shared/pushed branches: your teammates can see what was added and what was reverted. No force push, no rewritten history, no confusion.
Common revert commands:
git revert HEADโ revert the last commitgit revert <hash>โ revert a specific older commitgit revert -n <hash>โ revert but don't auto-commit (stage the reversal, let you review first)
The revert commit message is auto-generated: Revert "original message". You can edit it before saving.
# Revert the last commit
git revert HEAD
# Opens editor with: Revert "feat: add login"
# Save to create the revert commit.
# Revert a specific commit
git revert abc1234
# Revert without committing (review first)
git revert -n abc1234
git status # changes are staged but not committed
git commit -m "revert: remove broken feature"
# Revert multiple commits
git revert abc1234 def5678
# Revert a range (exclusive start, inclusive end)
git revert HEAD~3..HEAD
git reset moves the branch pointer backward, as if certain commits never happened. It is a time machine โ but a dangerous one.
Unlike revert, reset rewrites history. The undone commits disappear from git log. If you already pushed those commits, resetting and force-pushing will destroy your teammates' work.
Three modes of reset, each progressively more destructive:
git reset --soft HEAD~1โ moves branch pointer, keeps changes staged. Like "un-commit".git reset --mixed HEAD~1โ moves branch pointer, keeps changes in working directory unstaged. Like "un-commit + un-stage". This is the default.git reset --hard HEAD~1โ moves branch pointer, DELETES changes completely. Like "un-commit + throw away work".
Never use reset on shared/pushed branches. It requires force push and destroys team history. Reset is for local, unpushed commits only.
# --soft: Undo commit, keep changes staged
git reset --soft HEAD~1
git status # Changes are staged (green)
# --mixed: Undo commit, keep changes unstaged (default)
git reset --mixed HEAD~1
# OR just: git reset HEAD~1
git status # Changes are unstaged (red)
# --hard: Undo commit, DELETE ALL CHANGES
git reset --hard HEAD~1
git status # "nothing to commit, working tree clean"
# Your changes are GONE (unless saved in reflog)
# Reset multiple commits
git reset --soft HEAD~3 # undo last 3 commits
git reflog to find the lost commit hash, then git reset --hard <hash> to get it back. But don't rely on this โ always double-check before --hard.Think of Git having 3 areas: Repository (commits), Staging Area (index), Working Directory (your files on disk).
Each reset mode affects a different combination of these areas:
--soft: Moves Repository pointer. Staging and Working stay the same. Your work is preserved and ready to recommit.--mixed: Moves Repository pointer + resets Staging. Working stays the same. Your files are safe but need to be re-staged.--hard: Moves Repository pointer + resets Staging + resets Working. Everything gone.
Easy memory trick: --soft = safest (work preserved), --hard = harshest (work destroyed).
# Start with a commit that added "Hello World" to app.js
echo "Hello World" > app.js
git add app.js
git commit -m "add greeting"
# --soft: Commit undone, "Hello World" is still staged
git reset --soft HEAD~1
git status
# new file: app.js (in staging - green)
# Ready to recommit immediately.
# --mixed: Commit undone, "Hello World" is unstaged
git reset --mixed HEAD~1
git status
# Untracked files: app.js (in working dir - red)
# Need to git add before committing.
# --hard: Commit undone, app.js is DELETED
git reset --hard HEAD~1
git status
# nothing to commit, working tree clean
# app.js and its content are GONE from disk.
The golden rule of undoing commits: revert for public, reset for private.
Use git revert when:
- The commit is on a shared/pushed branch
- You want a record of the undo in history
- You are working with a team
Use git reset when:
- The commit is local and unpushed
- You want to clean up your own mess before anyone sees it
- You want to reorganize commits before sharing
If in doubt, use revert. It is always safe. Reset can always be replaced with revert โ the reverse is not true once you have pushed.
Never reset a commit that someone else might have pulled.
# SCENARIO 1: Local commit, not pushed -> RESET
git commit -m "WIP: broken experiment"
# I want to undo this and try again
git reset --soft HEAD~1
# Safe: only I have this commit locally.
# SCENARIO 2: Pushed commit, shared branch -> REVERT
git push origin main
# This commit broke production!
git revert HEAD
git push origin main
# Safe: team can see the fix in history.
# SCENARIO 3: Local feature branch, cleaning up -> RESET
git rebase -i HEAD~5
# Squashing WIP commits before PR
# Reset or rebase is fine here since branch is not shared yet.
# SCENARIO 4: Revert a merge commit
git revert -m 1 <merge-hash>
# -m 1 means "keep the first parent's history"
# Required for reverting merge commits.
main, develop, or any branch that others pull from โ always revert. If it is on your personal feature branch that nobody else uses โ reset is fine. When in doubt, revert.Reverting a merge commit requires the -m parent-number flag because merge commits have two parents โ Git needs to know which side of the fork to keep.
git revert -m 1 <merge-hash> โ revert a merge, keeping main's history (parent 1 is usually the branch you merged into).
Reverting a revert is a common pattern in release management. If you revert a feature for a release, then want to bring it back in the next release, you revert the revert commit.
git revert <revert-hash> โ this "re-reverts", effectively bringing the original changes back.
If a revert conflicts, it means the commit cannot be cleanly reversed. Resolve the conflict, then continue the revert.
# Revert a merge commit
git log --oneline
# abc1234 Merge pull request #42
# Must specify parent (-m flag)
git revert -m 1 abc1234
# -m 1 = keep main branch history, undo feature branch
# Reverting a revert (bringing changes back)
git log --oneline
# def5678 Revert "feat: add dashboard"
# abc1234 feat: add dashboard
git revert def5678
# This "re-reverts", effectively bringing dashboard back!
# Revert with conflict
git revert xyz1234
# CONFLICT (content): Merge conflict in app.js
# Resolve conflict, then:
git add app.js
git revert --continue
Lo kar liya โ Key Points:
- โ
git revert <hash>creates a new "anti-commit" that undoes changes โ safe for shared/pushed branches - โ
git resetmoves the branch pointer backward, erasing commits from history โ only for local/unpushed branches - โ
git reset --softkeeps changes staged,--mixedkeeps them unstaged,--harddeletes them entirely - โ Golden rule: revert for public/shared branches, reset for private/local branches
- โ
Reverting a merge commit requires
-m parent-numberflag to specify which parent to keep - โ Reverting a revert brings the original changes back โ common in release management
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login