You've spent this chapter learning to rewrite git history - rebase, interactive rebase, amend, cherry-pick. All powerful, all useful. Knowing when not to rewrite git history matters just as much, and it fits in one rule. If you remember nothing else from this chapter, keep this:
Never rewrite commits that other people have already pulled.
This lesson explains why, and how to stay safe.
Why rewriting shared commits breaks things
Every time you rewrite history, Git creates new commits with new IDs. You saw this
with rebase in rebase vs merge:
D and E became D' and E'.
That's harmless while the commits live only on your machine. But once you've pushed and a teammate has pulled, they have the old commit IDs. If you now rewrite those commits and force them onto the remote, two histories exist:
You (after rewrite): A - B - C' (new IDs)
Your teammate: A - B - C (old IDs they already have)
When your teammate pulls, Git can't tell that C and C' are "the same" work. It sees
two divergent histories and tries to reconcile them, producing duplicated commits and
ugly conflicts - for everyone, not just you. The shared branch becomes a mess.
The safe line: private vs public
The rule in practice comes down to one question: has anyone else seen these commits?
Safe to rewrite Not safe to rewrite
------------------------------- -------------------------------
Commits only on your machine Commits pushed to a shared branch
Your own feature branch that main / master / develop
nobody else has pulled A branch a teammate is using
Local work before first push Anything on a release branch
So cleaning up your own feature branch with git rebase -i before opening a pull
request is perfectly safe - it's still yours. Rebasing main after the team has been
building on it is not.
Force-push: the danger sign
Normally git push refuses to overwrite the remote if your history doesn't build
cleanly on top of it. That refusal is a safety feature - it's Git telling you your
push would erase commits.
After rewriting history, your local branch no longer matches the remote, so a plain push is rejected. The only way through is a force push:
git push --force
--force says "throw away whatever is on the remote and replace it with mine". On a
shared branch, that can silently delete a teammate's commits that were pushed after you
last pulled. There is no undo button on the remote for the people who lose work.
Use --force-with-lease instead
If you genuinely must force-push your own branch (for example, after squashing your feature branch before a PR), use the safer version:
git push --force-with-lease
--force-with-lease only overwrites the remote if it still looks the way you last saw
it. If someone else pushed in the meantime, the push is rejected instead of
destroying their work. Think of it as "force, but not if I'd be clobbering something new
I don't know about".
--force overwrite the remote, no questions asked (dangerous)
--force-with-lease overwrite only if nobody else pushed since I last looked (safer)
Make --force-with-lease your default whenever a force push is unavoidable.
One catch that surprises people: the lease compares against your remote-tracking
ref, not the live remote. So if you (or an editor plugin, or a git fetch you forgot you
ran) update that ref right before pushing, the lease now "sees" the newest state and
waves the push through, wiping the very commit it was meant to guard. Push soon after you
rebase, and don't fetch in between if you want the protection to mean anything.
Common mistake
Rebasing or amending a branch, seeing "push rejected", and reaching for git push --force to "make it work". If that branch is shared, you may have just erased a
colleague's commits. Stop and ask: is this branch only mine? If yes, prefer
--force-with-lease. If it's shared, don't rewrite it at all - use a
revert instead, which undoes a
change by adding a new commit rather than rewriting old ones.
The safe alternative for shared branches
When you need to undo something that's already public, don't rewrite - revert.
git revert creates a new commit that cancels out an old one, leaving history intact
and everyone's clones happy. It's the polite, non-destructive way to fix a shared
mistake, and you already met it in
reverting a commit.
FAQ
Is it ever OK to force-push?
Yes - onto your own branch that nobody else has pulled, typically after cleaning it up
before a pull request. Use --force-with-lease even then, so you can't accidentally
overwrite work you didn't know about.
What's the difference between --force and --force-with-lease?
--force overwrites the remote unconditionally. --force-with-lease overwrites only if
the remote still matches what you last fetched, so it refuses to destroy commits someone
else pushed in the meantime.
I already rebased a shared branch - what now?
Tell your team before anyone pulls again, and coordinate. The cleanest recovery is usually to agree on one correct version and have everyone reset to it. In future, reach for revert on shared branches instead of rewriting.
Why does Git reject my push after a rebase?
Because rewriting gave your commits new IDs, so your branch no longer builds on top of
the remote's history. The rejection is a safeguard. Only force-push (ideally
--force-with-lease) if the branch is truly yours alone.