A force push overwrites remote history with your local state. When done accidentally, it can erase team members' work and break deployments. Recovery is possible but requires immediate action.
The Scenario
git push --force origin main
# Oh no! You meant to push to your feature branch!Or:
``bash
git push -f
# You just overwrote 3 teammates' commits
The remote branch now points to your local commit, and previous commits may be orphaned.
Immediate Actions
Stop. Don't push anything else.
The commits still exist on the remote server temporarily, but they're now unreachable from any branch. They'll be garbage collected eventually (typically 30 days on most servers).
Notify your team immediately. Tell them: - Not to pull or fetch from the affected branch - Not to force push themselves - To preserve any local work they have
Diagnosis Steps
Check your local reflog:
``bash
git reflog show origin/main
Shows where the remote tracking branch pointed:
``
abc1234 origin/main@{0}: update by push
def5678 origin/main@{1}: update by push
ghi9012 origin/main@{2}: update by push
Identify the commit before force push:
``bash
git log def5678 --oneline -5
If you don't have the old commits locally:
Ask a teammate who hasn't pulled yet:
``bash
git log --oneline -10
They may still have the lost commits.
Check for GitHub/Bitbucket/GitLab backups: - GitHub: Check repository's "Actions" or "Pulse" for recent commits - GitLab: Check "Repository" > "Commits" history - Bitbucket: Check "Commits" tab for commit history
Solution 1: Recover from Local Reflog
If you have the old commits locally:
Find the commit before force push:
``bash
git reflog
Look for the state before your push:
``
abc1234 HEAD@{0}: push: force push
def5678 HEAD@{1}: pull: Merge pull request #42
Reset your local branch:
``bash
git checkout main
git reset --hard def5678
Force push to restore:
``bash
git push --force origin main
This restores the remote to its previous state.
Solution 2: Recover from Teammate's Clone
If you don't have the commits:
Have teammate with recent clone push:
``bash
# On teammate's machine
git checkout main
git log --oneline -5 # Verify they have the lost commits
git push --force origin main
Or have them create a bundle:
``bash
# On teammate's machine
git bundle create recovery.bundle main
Import the bundle:
``bash
# On your machine
git fetch recovery.bundle main:recovered-main
git checkout recovered-main
git push --force origin main
Solution 3: Recover via Server-Side Access
If you have server access:
SSH into Git server:
``bash
ssh git@your-git-server.com
cd /path/to/repo.git
Find lost commits:
``bash
git fsck --lost-found
Check for unreachable objects:
``bash
git log --reflog --all --oneline
Restore the branch:
``bash
git update-ref refs/heads/main abc1234
Solution 4: Recover from GitHub
GitHub keeps reflogs for branches:
Go to: Repository > Insights > Network
Or use the API:
``bash
gh api repos/:owner/:repo/events
Look for push events with the old commit SHAs.
Create a branch at the old commit:
``bash
gh api repos/:owner/:repo/git/refs -f ref=refs/heads/recovered-main -f sha=abc1234
Then promote to main:
``bash
git clone https://github.com/org/repo.git
cd repo
git checkout recovered-main
git push --force origin main
Solution 5: Recover from GitLab
GitLab has built-in recovery:
- 1.Via UI:
- 2.Go to Repository > Commits
- 3.Check "Show all branches and tags"
- 4.Find the lost commit SHA
- 5.Create branch from that commit
Via API:
``bash
curl --header "PRIVATE-TOKEN: <your-token>" \
"https://gitlab.com/api/v4/projects/:id/repository/commits"
Create branch:
``bash
curl --request POST --header "PRIVATE-TOKEN: <your-token>" \
"https://gitlab.com/api/v4/projects/:id/repository/branches?branch=recovered&ref=abc1234"
Solution 6: Partial Recovery - Cherry-Pick Lost Commits
If full recovery isn't possible, salvage individual commits:
Find commit SHAs from: - Email notifications - CI/CD logs - Issue/PR references - Team member memories
Cherry-pick them back:
``bash
git cherry-pick abc1234
git cherry-pick def5678
git push origin main
Verification
Verify restored commits:
``bash
git log --oneline -10
Check with team members:
``bash
# They should reset to match remote
git fetch origin
git reset --hard origin/main
Verify CI/CD passes: Trigger pipeline and confirm all tests pass.
Check for missing commits:
``bash
git log --all --oneline | wc -l
Compare with what should be there.
Prevention Measures
Use --force-with-lease Instead of --force
```bash # Dangerous git push --force origin main
# Safer - fails if remote has unexpected changes git push --force-with-lease origin main ```
--force-with-lease checks that the remote matches your expectations before forcing.
Configure Git to Reject Force Push
On the server (repo-specific):
``bash
git config receive.denyNonFastForwards true
On GitHub: Settings > Branches > Add rule > Require linear history
On GitLab: Settings > Repository > Protected Branches > Allowed to force push: No
Protect Important Branches
GitHub: Settings > Branches > Add rule - Require pull request reviews - Require status checks - Include administrators
GitLab: Settings > Repository > Protected Branches
Create Pre-Push Hooks
Create .git/hooks/pre-push:
```bash
#!/bin/bash
while read local_ref local_sha remote_ref remote_sha; do if [ "$remote_ref" = "refs/heads/main" ] || [ "$remote_ref" = "refs/heads/master" ]; then if git merge-base --is-ancestor $remote_sha $local_sha; then # Fast-forward, ok : else echo "ERROR: Attempted force push to $remote_ref" echo "Use --force-with-lease if you're sure." exit 1 fi fi done
exit 0 ```
Make it executable:
``bash
chmod +x .git/hooks/pre-push
Use Branch Naming Conventions
Never force push to:
- main or master
- develop
- release/*
- staging
Only force push to: - Your own feature branches - WIP branches
Configure Push Defaults
```bash # Prevent accidental pushes without upstream git config --global push.default current
# Always push to same-named branch git config --global push.autoSetupRemote true ```
Team Communication Template
When this happens, send a message like:
``` INCIDENT: Accidental force push to main branch
Timeline: [Time] Branch affected: main Estimated commits lost: [Number]
- 1.Actions:
- 2.DO NOT git pull or git fetch
- 3.Preserve your local main branch
- 4.Run: git log origin/main..main to see commits you have
Recovery in progress. Will update when resolved. ```