Someone Force Pushed Main
Force push disaster se recovery mushkil nahi, prevention aasan hai. Branch protection ON karo.
Force pushing to a shared branch (like main or develop) is one of the most destructive Git operations in a team environment.
It rewrites the remote history, making all teammates' local branches diverge from the remote. What was a clean linear history becomes a mess of conflicting commit hashes.
Common causes:
- Accidental force push — running
git push --forceinstead ofgit push(one flag difference, massive damage). - Rebasing a shared branch — someone rebases main and force pushes the result.
- Force push after amending — amending a pushed commit changes its hash, then force pushing overwrites the remote.
Symptoms teammates will see:
git pushrejected — "Updates were rejected because the tip of your current branch is behind"git pullcreates duplicate commits or confusing merge conflicts- CI/CD breaks because the commit history changed
Even if you quickly fix it, the disruption cascades — every developer's local repo is affected.
# How this disaster happens
git checkout main
# Someone rebases main (BAD IDEA on shared branch)
git rebase -i HEAD~5
# Squash some commits, reorder others...
# Now main has NEW commit hashes
git push origin main
# ERROR: Updates were rejected (remote has new commits too)
# The fatal mistake:
git push --force origin main
# Now the remote main has YOUR version of history
# All commits your teammates pushed are NO LONGER on remote main!
# Everyone who pulled those commits now has a diverged history.
--force flag can destroy hours of team work. The difference between git push and git push --force is the difference between a safe update and rewriting shared history. Never force push to a shared branch without coordinating with the entire team first.DON'T PANIC. The lost commits are still in the Git object store — both on the remote server and in teammates' local repos.
Step 1: Find the correct commit hash that main SHOULD point to.
If YOU force pushed: check your local reflog. git reflog will show the commit before your force push.
If SOMEONE ELSE force pushed: check git reflog on your local repo, or ask the team to check theirs. The old tip of main is recorded there.
GitHub also keeps a reflog-like record. You can check the GitHub events API or the repo's push log.
The correct commit is the one that was the tip of main BEFORE the force push.
# YOU force pushed and want to undo it:
# Step 1: Find the original commit
git reflog
# abc1234 HEAD@{0}: rebase -i (finished)
# def5678 HEAD@{1}: rebase -i (start)
# ghi9012 HEAD@{2}: pull origin main ← THIS was the original tip!
# jkl3456 HEAD@{3}: commit: team commit ← or this might be it
# Look for the last action BEFORE your rebase
# The hash from that line is the correct tip of main
# SOMEONE ELSE force pushed and you need to recover:
# If you had pulled recently, your reflog has the old tip
git reflog show origin/main
# ghi9012 refs/remotes/origin/main@{0}: pull origin main
# This is the last known good state of origin/main
# If you don't have it locally, check GitHub:
# Method 1: GitHub Push Events API
curl -H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/OWNER/REPO/events
# Look for the last push event before the force push
# Method 2: Ask a teammate who pulled recently
# They will have the correct commit in their reflog
git reflog show origin/main is the fastest way to find the last known good state.Once you have the correct commit hash, reset main to that commit and force push back.
git reset --hard <correct-hash> — moves main to the correct commit.
git push --force-with-lease origin main — force push with safety check.
--force-with-lease is critical: it checks that the remote ref matches your expectation, preventing you from overwriting someone else's recovery attempt.
This creates a NEW force push that restores the original history.
# Step 1: Reset main to the correct commit
git checkout main
git reset --hard ghi9012 # the correct hash from reflog
# Step 2: Verify this is the right commit
git log --oneline -5
# Should show the team's commits that were lost
# Step 3: Force push to restore
git push --force-with-lease origin main
# --force-with-lease ensures you don't overwrite
# another recovery attempt by someone else
# If --force-with-lease fails (someone else pushed):
git fetch origin
# Check what's on origin/main now
git log --oneline origin/main -5
# Coordinate with the team before force pushing again
# NEVER use bare --force for recovery:
git push --force origin main
# DANGEROUS — could overwrite another fix!
--force-with-lease vs --force: During a disaster, multiple people might try to fix it. --force blindly overwrites whatever is on the remote. --force-with-lease checks that the remote ref matches what you expect — if someone else already pushed a fix, it fails rather than overwriting their work. Always use --force-with-lease for recovery.After restoring main, every team member must sync their local repository.
Simply pulling will NOT work — it will try to merge the restored history with the forced history, creating duplicate commits.
Each team member must: 1) Save any local work (stash or branch), 2) Fetch the latest, 3) Reset their local main to match origin/main, 4) Re-apply their work.
This is disruptive, which is why preventing force pushes is so important.
# INSTRUCTIONS FOR EVERY TEAM MEMBER:
# Step 1: Save any local work
git stash
# OR create a backup branch
git branch my-work-backup
# Step 2: Fetch the restored remote
git fetch origin
# Step 3: Reset local main to match origin/main
git checkout main
git reset --hard origin/main
# Step 4: Re-apply your work
git stash pop
# OR rebase your feature branches onto the restored main
git checkout my-feature
git rebase origin/main
# Step 5: Verify everything is clean
git status
git log --oneline -5
# WARNING: If you had unpushed commits on main:
# They may be lost or duplicated
# Check your reflog and coordinate with the team
The best recovery is prevention. Never allow force pushes to shared branches.
GitHub: Settings → Branches → Branch protection rules → "Do not allow force pushes".
GitLab: Settings → Repository → Protected Branches → "Allowed to force push" = OFF.
Bitbucket: Repository settings → Branch permissions → "Prevent a force push".
Additionally, require pull requests for merging to main — this prevents direct pushes entirely.
Also consider: pre-receive hooks on the server that reject force pushes to protected branches.
# Prevention 1: GitHub Branch Protection (UI)
# Settings → Branches → Add rule → Branch name pattern: main
# ☑ Do not allow force pushes
# ☑ Require pull request reviews before merging
# ☑ Require status checks to pass before merging
# Prevention 2: GitHub CLI
gh api repos/:owner/:repo/branches/main/protection \
--method PUT \
--field required_pull_request_reviews='{"required_approving_review_count":1}' \
--field enforce_admins=true \
--field restrictions=null \
--field allow_force_pushes=false
# Prevention 3: Pre-receive hook (self-hosted Git server)
cat > custom_hooks/pre-receive << 'EOF'
#!/bin/bash
while read oldrev newrev refname; do
if [[ $refname == refs/heads/main ]]; then
base=$(git merge-base $oldrev $newrev 2>/dev/null)
if [[ $base != $oldrev ]]; then
echo "ERROR: Force push to main is not allowed"
exit 1
fi
fi
done
exit 0
EOF
# Prevention 4: Local config warning
git config --global push.requireForce true
# Warns when force pushing (doesn't prevent, but reminds)
Lo kar liya — Key Points:
- ✅ Force pushing to a shared branch rewrites remote history, causing all teammates' local repos to diverge
- ✅ Recovery starts with finding the correct commit hash using git reflog — the old tip of main is recorded there
- ✅ Use git reset --hard
to restore the branch, then git push --force-with-lease to push back - ✅ --force-with-lease is safer than --force because it checks that no one else has pushed to the remote
- ✅ All team members must reset their local main to match the restored remote — simple pulling creates duplicate commits
- ✅ Prevention is critical: enable branch protection rules to disable force pushes on main/develop
- ✅ The cost of a force push disaster is measured in team hours — prevention is always cheaper than recovery
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