Sometimes you need to completely remove commits from history - not just undo their effects, but erase them as if they never existed. This is where git reset --hard and force push come in. These are powerful tools that can save you from embarrassing mistakes, but they can also cause serious problems if misused.
Warning: The commands in this guide can permanently delete commits and cause problems for team members. Only use them when you fully understand the consequences!
What You'll Learn: In this comprehensive guide, you'll master:
- Understanding git reset modes (soft, mixed, hard)
- Using
git reset --hardto remove commits - When and how to force push safely
- The relationship between local and remote after reset
- Recovering from mistakes with
git reflog - Best practices for rewriting history
Understanding Git Reset Modes
Git reset has three modes that affect different areas:
+-------------------+-------------------+-------------------+
| REPOSITORY | STAGING AREA | WORKING DIRECTORY |
| (commits) | (index) | (files) |
+-------------------+-------------------+-------------------+
| | |
--soft resets --mixed resets --hard resets
(this only) (this + above) (all three)
The Three Reset Modes
| Mode | Repository | Staging Area | Working Directory | Use Case |
|---|---|---|---|---|
--soft | Reset | Keep | Keep | Redo commit message |
--mixed | Reset | Reset | Keep | Unstage files |
--hard | Reset | Reset | Reset | Complete undo |
Key Point: --hard is the only mode that affects your actual files. It will discard all uncommitted changes!
Scenario: Removing Commits with Reset
Let's walk through a real example. Here's our starting state:
git log
Output:
commit 6ba2cf9d542e08e35c3a44d18dc30d803e081acf (HEAD -> master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 15:00:47 2026 +0500
updated first.txt
commit ca0a0bc990527f72ae92d8a7f0cece1461d7684b
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 14:59:58 2026 +0500
updated third.txt
commit a2464fe62296925ce69366ea3c7d9de4fb95e29a
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 14:59:03 2026 +0500
added fourth.txt
commit ce0024b5fc0365ab9830c94eee7be465b141a617 (hello/master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 14:42:05 2026 +0500
Revert "added fourth.txt"
Current State Visualized
hello/master
|
ce0024b ----> a2464fe ----> ca0a0bc ----> 6ba2cf9
| | | |
"Revert "added "updated "updated
fourth.txt" fourth.txt" third.txt" first.txt"
|
HEAD
(master)
We want to go back to a2464fe (added fourth.txt), removing the last two commits.
Step 1: Perform the Reset
git reset --hard a2464fe
Output:
HEAD is now at a2464fe added fourth.txt
What Just Happened
Before reset --hard:
ce0024b ----> a2464fe ----> ca0a0bc ----> 6ba2cf9 (HEAD)
|
These commits exist
After reset --hard:
ce0024b ----> a2464fe (HEAD)
|
master
ca0a0bc ----> 6ba2cf9
|
These commits are ORPHANED
(still exist, but unreachable)
Step 2: Verify the Reset
git log
Output:
commit a2464fe62296925ce69366ea3c7d9de4fb95e29a (HEAD -> master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 14:59:03 2026 +0500
added fourth.txt
commit ce0024b5fc0365ab9830c94eee7be465b141a617
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date: Mon Jan 26 14:42:05 2026 +0500
Revert "added fourth.txt"
The two commits (ca0a0bc and 6ba2cf9) are gone from the visible history!
Check the Status
git status
Output:
On branch master
Your branch is behind 'hello/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
nothing to commit, working tree clean
Important: Git says we're "behind" the remote. This is because the remote still has the commits we just removed locally!
The Local vs Remote Problem
After reset, your local and remote are out of sync:
Your Local: GitHub (Remote):
a2464fe (HEAD) ce0024b
| |
master a2464fe
|
ca0a0bc
|
6ba2cf9 <-- hello/master
If you try a normal push:
git push
Output:
To https://github.com/owais-io/secondproject.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/owais-io/secondproject.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
Git refuses because pushing would lose commits on the remote.
Step 3: Force Push
To override the remote with your local state, use force push:
git push -f hello master
Or the longer form:
git push --force hello master
Output:
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To https://github.com/owais-io/secondproject.git
+ 6ba2cf9...a2464fe master -> master (forced update)
After Force Push
Your Local: GitHub (Remote):
a2464fe (HEAD) a2464fe <-- hello/master
| |
master (same!)
The remote now matches your local.
The old commits are GONE from both places.
Force Push Options
| Command | Description | Safety Level |
|---|---|---|
git push -f | Force push, overwrites remote | Dangerous |
git push --force | Same as -f | Dangerous |
git push --force-with-lease | Force push only if remote hasn't changed | Safer |
The Safer Option: --force-with-lease
git push --force-with-lease origin master
This only succeeds if no one else has pushed to the branch since your last fetch. It prevents accidentally overwriting a teammate's work.
Recovering Lost Commits with Reflog
Made a mistake? Git keeps a log of all HEAD movements:
git reflog
Output:
a2464fe (HEAD -> master) HEAD@{0}: reset: moving to a2464fe
6ba2cf9 HEAD@{1}: commit: updated first.txt
ca0a0bc HEAD@{2}: commit: updated third.txt
a2464fe (HEAD -> master) HEAD@{3}: commit: added fourth.txt
To recover the lost commits:
# Go back to the state before reset
git reset --hard 6ba2cf9
Lifesaver: Reflog entries are kept for about 90 days by default. If you accidentally reset, you can usually recover!
When to Use Reset + Force Push
Appropriate Situations
| Situation | Safe to Use? |
|---|---|
| Personal branch, only you use it | Yes |
| Removing sensitive data (passwords, keys) | Yes (then rotate credentials!) |
| Cleaning up messy commits before PR review | Yes |
| Feature branch before merge | Yes |
Dangerous Situations
| Situation | Safe to Use? |
|---|---|
| Shared branch (main, master, develop) | NO! |
| After others have pulled your commits | NO! |
| Production branch | NO! |
| When you're unsure | NO! |
Complete Workflow Example
Here's the full workflow from the terminal logs:
# 1. Check current state
git log --oneline
# 6ba2cf9 (HEAD -> master) updated first.txt
# ca0a0bc updated third.txt
# a2464fe added fourth.txt
# ce0024b (hello/master) Revert "added fourth.txt"
# 2. Push current state to remote (so both are in sync)
git push hello master
# 3. Now both local and remote have all commits
git log --oneline
# 6ba2cf9 (HEAD -> master, hello/master) updated first.txt
# 4. Reset to remove last 2 commits
git reset --hard a2464fe
# HEAD is now at a2464fe added fourth.txt
# 5. Local is now behind remote
git status
# Your branch is behind 'hello/master' by 2 commits
# 6. Normal push fails
git push
# ! [rejected] (non-fast-forward)
# 7. Force push to update remote
git push -f hello master
# + 6ba2cf9...a2464fe master -> master (forced update)
# 8. Verify both match
git log --oneline
# a2464fe (HEAD -> master, hello/master) added fourth.txt
Reset vs Revert: Decision Guide
Need to undo commits?
|
+---------------+---------------+
| |
Already pushed? Only local?
| |
+------+------+ Use git reset
| | (simpler)
Shared Personal
branch? branch?
| |
Use revert Can use either
(safe) (reset is cleaner)
Best Practices
1. Always Check Before Resetting
# See what you're about to lose
git log HEAD..origin/master --oneline
# Create a backup branch
git branch backup-before-reset
2. Communicate with Your Team
Before force pushing:
- Announce in your team channel
- Make sure no one has unpushed work
- Consider if revert is a better option
3. Use --force-with-lease
# Instead of
git push -f origin branch
# Use
git push --force-with-lease origin branch
4. Know Your Escape Route
# If something goes wrong
git reflog # Find the commit you want
git reset --hard <commit> # Go back
Quick Reference
# Reset modes
git reset --soft HEAD~1 # Undo commit, keep changes staged
git reset --mixed HEAD~1 # Undo commit, unstage changes (default)
git reset --hard HEAD~1 # Undo commit, discard all changes
# Reset to specific commit
git reset --hard <commit-hash>
# Force push
git push -f origin branch
git push --force origin branch
git push --force-with-lease origin branch # Safer
# Recovery
git reflog # See all HEAD movements
git reset --hard HEAD@{n} # Go back n steps
Summary
git reset --hard and force push are powerful tools for rewriting history:
- Reset --hard: Moves HEAD and discards commits (locally)
- Force push: Overwrites remote with your local history
- Use with caution: Can cause problems for teammates
- Prefer revert: For shared branches, use
git revertinstead - Know recovery: Use
git reflogif you make mistakes
Remember: With great power comes great responsibility. These commands can save you, but they can also cause chaos. Always think twice before rewriting shared history!

