Golden Rule of Rebasing
Rebasing is a powerful tool, but it has one rule you must never break. Almost every Git horror story — “my teammate force-pushed and I lost a day of work”, “the production branch got rewritten”, “our PR’s history is a mess” — comes from breaking this rule.
Why this rule exists
Rebasing creates new commits with new SHA hashes. The old commits are abandoned. If those old commits are public — already pushed and pulled by others — then your rebased history conflicts with theirs.
The disaster
Day 1: You push commits C, D, E to origin/main
teammates pull and get C, D, E
Day 2: You decide to "clean up history" with git rebase
New commits C', D', E' are created (different hashes)
Old commits C, D, E are now orphaned on your machine
Day 3: You force-push origin/main = C', D', E'
Now origin/main has different history than what teammates have
Day 4: Teammates pull. They have C, D, E. The remote has C', D', E'.
Their git pull tries to MERGE the two divergent histories.
Conflicts everywhere. Duplicate commits in their work.
Everyone loses a day untangling it.What is “safe to rebase”
Commits only on your local machine. Never pushed anywhere. Rebase as much as you like.
Branches that are explicitly yours. Personal feature branches that no one else has pulled.
Branches your team has agreed to rebase (e.g., a fork’s feature branch where you and one reviewer collaborate). Communicate before force-pushing.
What is NOT safe to rebase
main,master,develop— the main lines of work. Never.Release branches that others may have based hotfixes off.
Any shared branch that two or more people are actively committing to.
Public open-source contributions once the PR has been pushed — coordinate with the maintainer.
The safe-rebase checklist
Before you rebase
# 1. Confirm only YOU have this branch git branch -r --contains HEAD # Should show: origin/your-branch (and maybe nothing else) # 2. Check if anyone has pulled this branch recently # (look at PR history, ask in chat) # 3. If safe → rebase git rebase main # 4. Force-push WITH SAFETY git push --force-with-lease
What --force-with-lease does
--force-with-lease is the safe sibling of --force. It force-pushes only if the remote is in the state you expect (the last commit you fetched is still the tip). If someone else pushed in between, it refuses — protecting them from being overwritten.
Recommended
git push --force-with-lease # Old, dangerous form: git push --force # (no safety check; can erase other people's work)
The “rebase locally, merge to share” pattern
The cleanest workflow that respects the golden rule:
Work on a personal feature branch. Rebase, squash, and amend as much as you like LOCALLY.
When ready to share, push the branch and open a PR.
Reviews happen. You may still amend / rebase / force-push-with-lease here.
When merged: the PR creates a merge commit (or squash) on main. Main is never rebased.
If you broke the rule — how to recover
Teammate's perspective after you force-pushed
# Their next 'git pull' produces conflicts
# They can:
# Option 1: Rebase their local commits onto the new remote history
git fetch origin
git rebase origin/main
# Option 2: Reset their branch to match the remote (DESTRUCTIVE for them)
git reset --hard origin/main
# Option 3: Recover from reflog if work was lost
git reflog
git reset --hard HEAD@{pre-disaster-state}Communicating around rebases
Before force-pushing a shared feature branch, announce it: “Hey team, I’m about to rebase feature-x. Hold off on pulling until I’m done.”
After force-pushing, tell teammates: “Done — please
git fetchandgit reset --hard origin/feature-xto get the new history.”In open-source PRs, mention in the PR comments when you force-push so reviewers know the line-by-line comments may have moved.
Branch protection rules
Hosting platforms let you enforce the golden rule. On GitHub: Settings → Branches → Branch protection rules. Tick “Disallow force pushes” for main and any other protected branch. Now nobody can break the rule even by accident.
Shared branches: never rebase.
When in doubt: merge.
--force-with-lease your default by setting an alias: git config --global alias.pushf "push --force-with-lease". You’ll never accidentally use plain --force again.