Git Undo Changes, Reset, Restore, and Revert
1. How do you discard local changes and unstage a file using git restore?
What is git restore?
git restore is used to restore files in your working directory or remove files from the staging area.
Two common uses are:
- Discard modifications made to a file.
- Unstage a file that was added using
git add.
A. Discard Local Changes
Suppose you modified:
Login.cs
but have not staged it yet.
Check:
git status
You might see:
modified: Login.cs
To discard the changes:
git restore Login.cs
The file is restored to its version from the index (normally matching the last commit if you haven't staged anything).
To discard changes in all tracked files:
git restore .
Important
This can permanently discard uncommitted working-directory changes.
Modified Login.cs
↓
git restore Login.cs
↓
Changes discarded
B. Unstage a File
Suppose:
git add Login.cs
Now Login.cs is in the staging area.
To remove it from staging:
git restore --staged Login.cs
The changes remain in your working directory.
Working Directory
↓
git add
↓
Staging Area
↓
git restore --staged
↓
Working Directory
So:
git restore Login.cs
and:
git restore --staged Login.cs
perform very different operations.
Simple Comparison
| Command | Purpose |
|---|---|
git restore file |
Discard working-directory changes |
git restore --staged file |
Unstage file but keep changes |
git restore . |
Discard changes in all tracked files |
Key Points
git restoreworks mainly with file-level changes.git restore --stagedremoves a file from staging.- Unstaging does not delete your modifications.
git restore <file>can discard uncommitted modifications, so use it carefully.- It does not remove untracked files.
2. What is the difference between git reset and git revert?
Both commands can be used to undo changes, but they work very differently.
git reset
git reset moves the current branch/HEAD to another commit and can also modify the staging area and working directory depending on the mode.
Suppose:
A --- B --- C
HEAD
Execute:
git reset --hard B
Now:
A --- B
HEAD
From the branch's visible history, commit C has been removed.
Therefore, reset is commonly used for local/unpublished history.
git revert
git revert does not remove the old commit.
Instead, it creates a new commit that reverses the changes introduced by an earlier commit.
Before:
A --- B --- C
Execute:
git revert C
After:
A --- B --- C --- D
Here:
C = original commit
D = commit reversing C
The history is preserved.
That makes revert generally safer for commits already shared with other developers.
reset vs revert
git reset |
git revert |
|---|---|
| Moves branch/HEAD backward | Creates a new undo commit |
| Can rewrite branch history | Preserves existing history |
| Useful for local/unpublished commits | Useful for shared/pushed commits |
Can discard changes with --hard |
Does not delete the original commit |
| May require force push if history was already pushed | Normally allows a regular push |
Key Points
A useful rule is:
Not pushed
↓
reset can be appropriate
Already pushed/shared
↓
revert is usually safer
3. What is the difference between git reset --soft, --mixed, and --hard?
The main difference is what Git does with:
- Commit history /
HEAD - Staging area
- Working directory
Suppose:
A --- B --- C
HEAD
You want to reset from C back to B.
git reset --soft
git reset --soft HEAD~1
This moves HEAD back one commit but keeps the changes staged.
Commit C removed from branch history
↓
Changes remain staged
You can immediately recommit:
git commit -m "New commit message"
When useful
For example, you committed too early or want to recreate the last commit differently.
git reset --mixed
git reset --mixed HEAD~1
Or simply:
git reset HEAD~1
--mixed is the default.
It:
- Moves
HEADback. - Removes the changes from staging.
- Keeps the changes in your working directory.
Commit C removed
↓
Changes unstaged
↓
Changes still available locally
You can modify the files again and selectively stage them.
git reset --hard
git reset --hard HEAD~1
It:
- Moves
HEADback. - Resets the staging area.
- Resets tracked files in the working directory.
Commit C removed
↓
Staging changes removed
↓
Working-directory changes removed
Use this carefully because it can discard work.
Simple Comparison
| Reset Type | Commit | Staging Area | Working Files |
|---|---|---|---|
--soft |
Reset | Changes kept staged | Changes kept |
--mixed |
Reset | Changes unstaged | Changes kept |
--hard |
Reset | Reset | Tracked changes discarded |
An easy way to remember:
--soft
Keep everything staged
--mixed
Keep changes, but unstage them
--hard
Discard tracked changes too
Key Points
--soft→ safest when you want to recommit immediately.--mixed→ keeps your code but unstages it.--hard→ can discard local work.--mixedis the default forgit reset.HEAD~1means one commit before the current commit.
4. How do you undo the last commit before it has been pushed?
If the commit has not been pushed, git reset is commonly used.
Suppose:
A --- B --- C
HEAD
You accidentally committed C.
Option 1: Undo Commit but Keep Changes Staged
git reset --soft HEAD~1
Result:
A --- B
HEAD
Changes from C → staged
This is useful when you want to correct something and recommit.
For example:
git reset --soft HEAD~1
git commit -m "Correct commit message"
If you only need to change the latest commit message, an even simpler command is:
git commit --amend -m "Correct commit message"
Option 2: Undo Commit and Unstage Changes
git reset HEAD~1
Equivalent to:
git reset --mixed HEAD~1
The commit is undone, but your file changes remain.
Option 3: Undo Commit and Discard Changes
git reset --hard HEAD~1
This removes the commit and discards its tracked file changes from the working tree.
Use this only when you are sure you do not need the changes.
Key Points
For an unpushed last commit:
Keep staged
↓
git reset --soft HEAD~1
Keep changes but unstage
↓
git reset --mixed HEAD~1
Discard changes
↓
git reset --hard HEAD~1
5. How do you safely undo a commit that has already been pushed to a shared branch?
For a commit already pushed to a shared branch, the normal safe approach is:
git revert <commit-id>
Why?
Because other developers may already have the original commit.
Suppose the remote history is:
A --- B --- C
Other developers may already have pulled C.
If you reset:
git reset --hard B
and force-push, you rewrite the shared branch history.
That can cause synchronization problems for other developers.
Instead, use:
git revert C
Git creates:
A --- B --- C --- D
where:
D = reverses the changes introduced by C
Then push normally:
git push
Simple Example
Check the commit history:
git log --oneline
Example:
8a25c10 Add payment feature
7b12d40 Add login feature
3c21a11 Initial commit
Suppose the payment commit must be undone:
git revert 8a25c10
Git creates a new revert commit.
Then:
git push
History becomes conceptually:
Initial
↓
Login
↓
Payment
↓
Revert Payment
The original payment commit remains in the history, but its changes are reversed.
Why Not reset --hard?
Avoid doing this casually on a shared branch:
git reset --hard HEAD~1
git push --force
because it rewrites published history and can interfere with other developers' work.
Key Points
- For local/unpushed history,
resetcan be appropriate. - For pushed/shared history, prefer
revert. revertcreates a new commit.- The original commit remains visible.
- Revert is collaboration-friendly because it does not normally rewrite existing shared history.
Quick Interview Summary
Discard file changes
→ git restore file
Unstage file
→ git restore --staged file
Undo unpushed commit, keep staged
→ git reset --soft HEAD~1
Undo unpushed commit, keep unstaged
→ git reset --mixed HEAD~1
Undo unpushed commit and discard changes
→ git reset --hard HEAD~1
Undo pushed/shared commit safely
→ git revert <commit-id>