Rebase & History

Rewriting commits for a clean history — and the golden rule that keeps teams from losing work

Why

Rebase is how teams maintain a clean, bisectable linear history instead of a tangle of merge commits. But rebasing rewrites commit SHAs — force-pushing a rebased branch that others have already pulled causes their history to diverge and creates painful conflicts. The golden rule of rebase is the most important thing to understand before using it.

Interactive rebase is one of the most powerful pre-review tools — squashing WIP commits, reordering, or rewriting messages before opening a PR is standard practice in professional teams.

Intuition

Rebase takes each commit on your branch and replays it, one at a time, on top of the target commit. Because commits are immutable, this creates brand-new commit objects with new SHAs — the code changes are identical, but the parent pointers are different. The original commits still exist until garbage-collected.

Merge preserves history honestly; rebase rewrites it to look clean. Both have legitimate uses — the choice depends on whether the branch is shared.

Explanation
git rebase and the golden rule
# Update a feature branch with the latest main git switch feature git rebase main # replays feature commits on top of main's tip # feature commits get new SHAs # Conflict during rebase git add resolved-file.py git rebase --continue # replay the next commit git rebase --abort # abandon and restore original state # THE GOLDEN RULE: # Never rebase commits that have already been pushed to a branch # others are using. Force-pushing rewritten history causes their # local copies to diverge — their commits won't exist in yours.

Private branches only you have pulled are safe to rebase and force-push. Shared branches — main, develop, release — must never be rebased.

Interactive rebase — cleaning up before review
# Rewrite the last 4 commits interactively git rebase -i HEAD~4 # In the editor, each commit has a command: # pick → keep as-is # reword → keep, edit the message # squash → fold into previous commit (combine messages) # fixup → fold into previous commit (discard this message) # drop → delete this commit entirely # Typical pre-PR workflow: # pick a1b2c3d feat: add user authentication # fixup e4f5g6h fix typo in auth.py # fixup i7j8k9l make tests pass # → results in one clean, meaningful commit

Interactive rebase turns "wip", "fix", "oops" commits into clean history before review. The reviewer sees your intent, not your process.

Rebase vs merge — when to use each
Use merge when
Preserving the exact history of when branches combined matters. Integrating into shared branches via PR. Long-lived feature branches where the merge commit itself is meaningful.
Use rebase when
You want a clean linear history. Updating a private feature branch with the latest main before a PR. Squashing WIP commits. The commits have not been shared with anyone else.