Home DevOps Git Push — The Force Push That Deleted a Colleague's Commits
Beginner 3 min · July 07, 2026

Git Push — The Force Push That Deleted a Colleague's Commits

A developer used git push --force on a shared branch, silently deleting a teammate's commits.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Production
production tested
July 07, 2026
last updated
214
articles · all by Naren
Before you start⏱ 20 min
  • Basic programming fundamentals (variables, functions, control flow)
  • No prior devops experience needed — we start from first principles
 ● Production Incident
Quick Answer

git push origin main pushes the local main branch to origin. git push -u origin feature sets upstream tracking. git push --force-with-lease force-pushes safely (fails if remote has unknown commits). NEVER use --force alone.

✦ Definition~90s read
What is Git Push?

git push uploads local commits to a remote repository and updates remote branches. It is the command that makes your work visible to the team.

Git push is like publishing a book.
Plain-English First

Git push is like publishing a book. You've been writing locally (committing), and now you're sending the finished chapters to the publisher (remote). git push origin main says 'here are my latest chapters for the main book.' If someone else published chapters while you were writing, --force-with-lease says 'only publish if my copy matches what I last saw' — preventing accidental overwrites. Plain --force says 'publish regardless of what anyone else wrote' — which is how chapters get lost.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Git push is the most dangerous common Git command. A wrong push can overwrite teammates' commits, rewrite published history, and break CI/CD pipelines. The safety mechanisms (--force-with-lease, branch protection, pre-push hooks) are not enabled by default. Every developer has a force-push horror story. This guide covers safe push practices, the anatomy of a force-push disaster, and how to recover when things go wrong.

Push Basics: Upstream, Tracking, and Safe Force Push

git push origin main pushes local main to remote origin's main. git push -u origin feature pushes and sets upstream tracking — after this, simply git push works. git remote -v shows configured remotes.

git push --force-with-lease is the ONLY safe way to force-push. It checks that the remote branch is at the commit you expect before overwriting. If another developer pushed, --force-with-lease fails with a warning. git config --global push.forceWithLease true makes even --force behave like --force-with-lease.

git push --dry-run shows what would happen without sending. git push --delete origin <branch> deletes a remote branch. git push --tags pushes tags that git push doesn't send by default.

01_push_basics.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# First push — set upstream
git push -u origin feature/payment-v2

# Subsequent pushes (upstream configured)
git push

# Push specific branch to specific remote
git push origin main

# Safe force push after rebase
git push --force-with-lease origin feature/payment-v2

# Make --force default to --force-with-lease
git config --global push.forceWithLease true

# Dry run — see what would push
git push --dry-run origin main

# Delete remote branch
git push origin --delete feature/old

# Push tags
git push origin --tags
Output
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 456 bytes | 456.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To github.com:user/repo.git
a1b2c3d..d4e5f6g main -> main
Never Use `--force` Without `--force-with-lease`
git push --force unconditionally overwrites the remote branch. It does not check if the remote has advanced since your last fetch. --force-with-lease checks the remote's expected state and fails if unknown commits exist. Configure push.forceWithLease true globally as a safety net. Branch protection rules on GitHub/GitLab can prevent force-pushes entirely for specific branches.
Production Insight
Configure push.forceWithLease globally on every developer machine. On CI, require git push --force-with-lease for any deploy workflow that modifies release branches. Use GitHub branch protection rules: set 'Allow force pushes' to 'Everyone' with 'Force push bypasses pull request requirement' unchecked — this prevents force-pushes to protected branches entirely.
Key Takeaway
Always set upstream with -u on first push. Use --force-with-lease not --force. Configure push.forceWithLease true globally. Branch protection rules prevent force-pushes to main/release.

Recovering from a Bad Push

If you force-pushed and lost commits: 1. Anyone who has the commits locally can re-push them. 2. Check their local reflog: git reflog | grep <branch-name>. 3. Recreate the branch from the found SHA: git branch recovered <sha>. 4. Push the recovered branch: git push origin recovered.

If the bad push was a force-push that deleted a remote branch: use GitHub's 'Restore branch' button (Settings → Branches → 'View branches' → find deleted branch → Restore). For GitLab, use the 'Restore branch' button in Repository → Branches.

If you pushed sensitive data (API keys, passwords): assume it's compromised. Rotate the key immediately. Use git filter-branch or git filter-repo to remove the data from history, then force-push with lease. Notify your security team.

02_push_recovery.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Recover from bad force-push (someone has the commits locally)
git reflog | head -10
git branch recovered-feature a1b2c3d
git push origin recovered-feature

# Fix pushed sensitive data with filter-repo
git filter-repo --path .env --invert-paths
git remote add origin <url>
git push --force-with-lease origin main

# Undo a push that introduced a bad commit
git revert <bad-commit-sha>
git push origin main

# Create an annotated tag for recovery point
git tag -a before-push-$(date +%Y%m%d) -m 'Recovery checkpoint'
git push origin before-push-20260707
Output
a1b2c3d HEAD@{0}: push: feature/payment-v2
d4e5f6g HEAD@{1}: commit: Add validation
Remote branch restored. 3 commits recovered.
To github.com:user/repo.git
a1b2c3d..d4e5f6g recovered-feature -> recovered-feature
Production Insight
As part of your incident response runbook, include a 'Bad Push Recovery' section: 1. git reflog on every developer's machine to find the SHA. 2. git push --delete origin <branch> to remove the corrupted remote branch. 3. git push origin <branch> from the developer who has the good copy. 4. Notify the team that the branch was force-recovered. For CI/CD, always use git push --force-with-lease in deploy scripts — never plain --force.
Key Takeaway
Recover lost commits via reflog. Use git revert for bad commits (not force-push). For sensitive data, use git filter-repo. Tag recovery checkpoints before risky operations.
● Production incidentPOST-MORTEMseverity: high

The Force Push That Silently Deleted 3 Commits

Symptom
Two developers were working on the same feature branch. Developer A rebased their local branch and used git push --force to update the remote. Developer B had pushed 3 commits that Developer A didn't know about. The force push silently deleted Developer B's 3 commits from the remote. The team discovered the missing commits 3 days later during QA.
Assumption
Developer A assumed that since they were the only one working on the branch, force push was safe. They did not communicate with Developer B before pushing. They used --force instead of --force-with-lease.
Root cause
1. Developer A and Developer B were both working on feature/payment-v2. 2. Developer A rebased their local branch onto main (legitimate rebase to resolve conflicts). 3. While A was rebasing, Developer B pushed 3 commits to the remote: 'Add validation', 'Fix timeout', 'Add logging'. 4. Developer A ran git push --force origin feature/payment-v2. 5. --force unconditionally overwrote the remote branch with A's local state. 6. Developer B's 3 commits existed only locally on B's machine. 7. Developer B's next git pull produced 'diverged branches' error. 8. B assumed it was a local issue and spent 2 hours debugging before realizing commits were missing from remote. 9. Three days later, QA noticed 'Add validation' was missing from the deployed feature.
Fix
1. Immediate: Developer B recovered their commits from local reflog and re-pushed to a new branch. 2. Cherry-picked B's commits onto A's branch to restore them. 3. Both devs switched to git config --global push.forceWithLease true which makes --force default to --force-with-lease. 4. Team rule: announce before force-pushing to shared branches. 5. Branch protection: added rule to require PR review before merging to feature/* branches. 6. Added CI check: git push --dry-run step that warns if branch has diverged from remote.
Key lesson
  • NEVER use git push --force.
  • Always use --force-with-lease which fails if the remote has commits you don't know about.
  • Communicate before force-pushing shared branches.
  • Configure push.forceWithLease globally.
  • Branch protection rules prevent force-pushes entirely.
Production debug guideUse this when production is on fire5 entries
Symptom · 01
Push a branch to remote for the first time
Fix
Run git push -u origin <branch> — sets upstream tracking
Symptom · 02
Push with upstream already configured
Fix
Run git push — uses upstream branch from -u
Symptom · 03
Re-push after rebasing (force required)
Fix
Run git push --force-with-lease — NEVER --force
Symptom · 04
Delete a remote branch
Fix
Run git push origin --delete <branch>
Symptom · 05
See what would be pushed without sending
Fix
Run git push --dry-run origin main
⚙ Quick Reference
2 commands from this guide
FileCommand / CodePurpose
01_push_basics.shgit push -u origin feature/payment-v2Push Basics
02_push_recovery.shgit reflog | head -10Recovering from a Bad Push

Key takeaways

1
git push -u origin <branch> sets upstream tracking on first push
2
Always use --force-with-lease, NEVER plain --force
3
Configure push.forceWithLease true globally
4
Branch protection rules prevent force-pushes to protected branches
5
Recover lost commits via reflog on the developer who has them
6
Use git revert for fixing bad commits on shared branches, not reset+force-push
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 07, 2026
last updated
214
articles · all by Naren
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Log: Master Commit History Search and Filtering
44 / 47 · Git
Next
Git Remote: Manage Repository Connections Safely