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

bash
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. 1.Via UI:
  2. 2.Go to Repository > Commits
  3. 3.Check "Show all branches and tags"
  4. 4.Find the lost commit SHA
  5. 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. 1.Actions:
  2. 2.DO NOT git pull or git fetch
  3. 3.Preserve your local main branch
  4. 4.Run: git log origin/main..main to see commits you have

Recovery in progress. Will update when resolved. ```