Force Push: When It's Allowed and When It's Not
A real teammate's work, genuinely at risk. See --force actually destroy it — then the safer real alternative.
feature/dinner-plans is genuinely shared — Frank Chen pushed his own real commit to it while you were working locally, completely unaware. Your own local history has since moved too. A plain push gets genuinely rejected — git's real, built-in safety net catching exactly this situation.\n\nThis lesson shows, live, on a disposable throwaway clone, exactly what plain --force would actually do: genuinely destroy Frank's real commit, no warning, no trace. Then it shows the real, safe alternative — --force-with-lease — correctly refusing the exact same push, because it can see the remote has moved since you last looked.
A Real, Safe Rejection
git push
! [rejected] (fetch first)
git's real, built-in safety net: it will never silently overwrite real remote work it does not know about.
Watching --force Actually Destroy It
cp -r ~/team-recipes ~/team-recipes-danger-demo
cp -r ~/recipes-origin.git ~/recipes-origin-danger-demo.git
cd ~/team-recipes-danger-demo && git remote set-url origin ../recipes-origin-danger-demo.git
git push --force
A real, genuinely disposable copy — its own clone AND its own origin, so the real shared repository is never touched. On it: Frank's genuine commit, gone, no trace, no warning.
The Real, Safe Alternative
cd ~/team-recipes && git push --force-with-lease
! [rejected] (stale info)
Refused — because --force-with-lease genuinely checks that the remote hasn't moved since you last saw it, before ever overwriting anything.
Resolving It for Real, Then Pushing Safely
git fetch origin
git rebase origin/feature/dinner-plans
# ...a real conflict, resolved by hand...
git rebase --continue
git push --force-with-lease
Now safe and correct: local genuinely contains Frank's real commit too, so --force-with-lease has nothing real left to protect against.
git push --force
Genuinely overwrites the remote branch with local history, no matter what real work is already there — confirmed directly in this lab actually destroying a real teammate's commit on a disposable clone, with zero warning.
git push --force-with-lease
Only overwrites the remote if it still genuinely matches what your local repository last saw — confirmed directly in this lab refusing the exact same push once the remote had real, new work it didn't know about.
📝 Add Real Work, Unaware of the Real Conflict
Add a genuine new commit — Frank's parallel push already happened, you just don't know it yet.
cd ~/team-recipes && printf '\nWednesday: soup night.\n' >> dinner-plans.mdcd ~/team-recipes && git add dinner-plans.mdcd ~/team-recipes && git commit -m 'Add Wednesday soup nite'cd ~/team-recipes && git commit --amend -m 'Add Wednesday soup night'cd ~/team-recipes && git pushstudent@lab:~$ cd ~/team-recipes && printf '\nWednesday: soup night.\n' >> dinner-plans.md student@lab:~$ cd ~/team-recipes && git add dinner-plans.md student@lab:~$ cd ~/team-recipes && git commit -m 'Add Wednesday soup nite' [feature/dinner-plans 0653509] Add Wednesday soup nite 1 file changed, 2 insertions(+) student@lab:~$ cd ~/team-recipes && git commit --amend -m 'Add Wednesday soup night' [feature/dinner-plans 489a790] Add Wednesday soup night Date: Mon Aug 24 19:59:15 2026 +0000 1 file changed, 2 insertions(+) student@lab:~$ cd ~/team-recipes && git push To ../recipes-origin.git ! [rejected] feature/dinner-plans -> feature/dinner-plans (fetch first) error: failed to push some refs to '../recipes-origin.git' hint: Updates were rejected because the remote contains work that you do not hint: have locally. This is usually caused by another repository pushing to hint: the same ref. If you want to integrate the remote changes, use hint: 'git pull' before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
💥 Watch --force Actually Destroy It
On a real, disposable copy — never the real thing — see exactly what plain --force does.
cp -r ~/team-recipes ~/team-recipes-danger-democp -r ~/recipes-origin.git ~/recipes-origin-danger-demo.gitcd ~/team-recipes-danger-demo && git remote set-url origin ../recipes-origin-danger-demo.gitcd ~/team-recipes-danger-demo && git push --forcecd ~/team-recipes && git push --force-with-leasestudent@lab:~$ cp -r ~/team-recipes ~/team-recipes-danger-demo student@lab:~$ cp -r ~/recipes-origin.git ~/recipes-origin-danger-demo.git student@lab:~$ cd ~/team-recipes-danger-demo && git remote set-url origin ../recipes-origin-danger-demo.git student@lab:~$ cd ~/team-recipes-danger-demo && git push --force Enumerating objects: 7, done. Counting objects: 100% (7/7), done. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 583 bytes | 53.00 KiB/s, done. Total 6 (delta 2), reused 0 (delta 0), pack-reused 0 To ../recipes-origin-danger-demo.git + a6c855b...489a790 feature/dinner-plans -> feature/dinner-plans (forced update) student@lab:~$ cd ~/team-recipes && git push --force-with-lease To ../recipes-origin.git ! [rejected] feature/dinner-plans -> feature/dinner-plans (stale info) error: failed to push some refs to '../recipes-origin.git'
🔧 Resolve the Real Conflict, Then Push Safely
Fetch Frank's real commit, integrate it for real, then force-with-lease has nothing left to protect against.
cd ~/team-recipes && git fetch origincd ~/team-recipes && git rebase origin/feature/dinner-planscd ~/team-recipes && printf 'Dinner Plans\n\nMonday: pasta night.\n\nTuesday: taco night.\n\nWednesday: soup night.\n' > dinner-plans.mdcd ~/team-recipes && git add dinner-plans.mdcd ~/team-recipes && git rebase --continuecd ~/team-recipes && git push --force-with-leasestudent@lab:~$ cd ~/team-recipes && git fetch origin remote: Enumerating objects: 5, done. remote: Counting objects: 100% (5/5), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), 296 bytes | 24.00 KiB/s, done. From ../recipes-origin 66c3f7a..a6c855b feature/dinner-plans -> origin/feature/dinner-plans student@lab:~$ cd ~/team-recipes && git rebase origin/feature/dinner-plans Auto-merging dinner-plans.md CONFLICT (content): Merge conflict in dinner-plans.md error: could not apply 489a790... Add Wednesday soup night hint: Resolve all conflicts manually, mark them as resolved with hint: "git add/rm <conflicted_files>", then run "git rebase --continue". hint: You can instead skip this commit: run "git rebase --skip". hint: To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply 489a790... Add Wednesday soup night student@lab:~$ cd ~/team-recipes && printf 'Dinner Plans\n\nMonday: pasta night.\n\nTuesday: taco night.\n\nWednesday: soup night.\n' > dinner-plans.md student@lab:~$ cd ~/team-recipes && git add dinner-plans.md student@lab:~$ cd ~/team-recipes && git rebase --continue [detached HEAD b135677] Add Wednesday soup night 1 file changed, 2 insertions(+) Successfully rebased and updated refs/heads/feature/dinner-plans. student@lab:~$ cd ~/team-recipes && git push --force-with-lease Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 333 bytes | 83.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 To ../recipes-origin.git a6c855b..b135677 feature/dinner-plans -> feature/dinner-plans
Lab 2.6.3 complete. A real teammate's commit, genuinely at risk — and the safe way through:\n\n\n Real safety-net rejection seen : ✅ plain push\n --force genuinely destroyed it : ✅ disposable clone only\n --force-with-lease refused it : ✅ real repository\n Real conflict resolved, pushed : ✅ force-with-lease succeeds\n
Enable JavaScript to run the live terminal and track your progress.