Git Rebase
Replay commits onto a new base, edit a local series, and refuse rebase on published history that other people already use.
Rebase is a controlled history rewrite
What rebase actually changes
Suppose feature/search contains commits C and D based on A while main has advanced through B. Rebasing finds the branch-specific changes, resets the feature context to B, and replays those changes as C′ and D′.
A—B main
A—C—D feature/search. The histories diverge at A.
A—B—C′—D′ feature/search
C′ and D′ express similar patches but have new parents, timestamps, and object IDs.
Choose rebase or merge by ownership and evidence
Rebase private work
You alone use the unpublished branch and want to update its base or prepare a coherent review series.
Merge shared work
The commits are already integrated, reviewed by hash, or used as a base by teammates or automation.
Keep meaningful topology
A merge commit can preserve when parallel work converged and which exact histories were integrated.
Follow repository policy
Some teams require merge commits, squash merges, or rebased pull requests. Tool capability does not override that contract.
“Linear history is cleaner” is not enough by itself. A graph is useful when it accurately represents collaboration and supports debugging.
Run a rewrite preflight
git status
git branch --show-current
git fetch origin
git log --oneline --decorate --graph --all
git log origin/main..feature/search --oneline
git log feature/search..origin/main --oneline
git diff origin/main...feature/search
git branch --contains feature/search- 01Require a clean state
Commit, stash, or move unrelated work before changing commit ancestry.
- 02Confirm ownership
Know whether anyone fetched, reviewed by hash, or based work on these commits.
- 03Update remote knowledge
Fetch before choosing the new base or making publication decisions.
- 04Record the old tip
Create a named backup ref and save the graph before a substantial rewrite.
Replay a private branch onto current main
git status
git fetch origin
git log --oneline --decorate --graph --all
# While on the private feature branch:
git switch feature/search
git branch backup/feature-search-before-rebase
git rebase origin/mainIf every patch applies, the feature ref advances to the final replacement commit. Main does not move. The backup ref preserves the original series for comparison and recovery.
Test what you learned
Type the command that replays the current private feature branch onto origin/main.
Resolve conflicts one replayed commit at a time
Rebase may stop while applying a specific commit. Read git status and the stopped commit, then resolve its intended change in the context of the updated base.
git status
# Resolve the current replayed commit, then:
git add <resolved-paths>
git diff --cached
git diff --cached --check
git rebase --continue
# Or cancel the entire operation:
git rebase --abortgit rebase --continue records the current replacement commit and advances to the next one. You may need to resolve similar areas more than once because each original commit is replayed separately.
Understand continue, skip, and abort
git rebase --continue
Use after staging the verified resolution for the current replayed commit.
git rebase --skip
Omits the current commit. Use only when its complete effect is redundant or intentionally unwanted—and prove that claim.
git rebase --abort
Cancels the operation and returns the branch to its pre-rebase tip.
git rebase --quit
Stops rebasing without resetting HEAD and the working state. This advanced escape differs materially from abort.
Open an interactive plan
git branch backup/feature-search-before-edit
git rebase -i HEAD~4The todo list is ordered oldest first because Git will replay it from top to bottom. Changing a line changes the operation; changing line order changes commit order.
pick a1b2c3d add search form
reword b2c3d4e validate empty queries
fixup c3d4e5f fix typo
edit d4e5f6a add result cachingpick / reword
Keep a commit as-is, or keep its patch while editing only the message.
edit
Pause after applying the commit so you can amend or split it.
squash / fixup
Combine with the previous todo commit; squash edits combined messages, while fixup normally discards the fixup message.
drop
Remove a commit from the replacement series. Verify its behavior is truly unwanted or represented elsewhere.
exec
Run a command at a chosen point, such as a focused test, and stop if it fails.
reorder
Move lines only when dependencies permit each intermediate commit to remain coherent.
Amend or split a stopped commit
When interactive rebase stops at edit, HEAD is the replacement version of that commit. Amend it, test, and continue.
# Amend content and/or message
git status
git add <paths>
git commit --amend
git rebase --continue
# Or split the stopped commit into smaller commits
git reset HEAD^
git add <first-paths>
git commit -m "Add search result cache"
git add <remaining-paths>
git commit -m "Test cache invalidation"
git rebase --continueAfter the mixed reset, the old commit’s changes remain in the working tree. Build replacement commits with deliberate staging, tests, and messages. Do not continue with valuable changes left unstaged accidentally.
Use fixup commits without hiding review
git commit --fixup=<target-commit>
git rebase -i --autosquash <base-commit>Autosquash rearranges marked fixup commits beside their targets in the todo list. Review the generated plan before saving; matching a target does not prove the combined patch is correct.
Move only the intended subseries with --onto
The general form is git rebase --onto <new-base> <old-base> <branch>. Git selects commits reachable from the branch but not from the old base and replays them onto the new base.
# Move feature/ui commits off feature/api and onto main
git branch backup/feature-ui-before-onto feature/ui
git rebase --onto main feature/api feature/uiCompare old and new series
git status
git log --oneline --decorate --graph --all
git range-diff origin/main...backup/feature-search-before-rebase \
origin/main...feature/search
git diff backup/feature-search-before-rebase..feature/search
git diff origin/main...feature/search
# Run relevant tests and whitespace checks
git diff origin/main...feature/search --checkgit range-diff compares two commit series and helps identify reordered, modified, added, or dropped patches. A zero tree diff between old and new tips can prove identical final content, but not identical intermediate commits or test behavior.
SERIESExpected commit mappingEvery old patch is accounted for as retained, edited, combined, or intentionally dropped.
TREEExpected final contentTip-to-tip and base-to-tip diffs show no unexplained change.
BUILDRelevant checks passTests validate both final behavior and important intermediate commits when bisectability matters.
GRAPHCorrect new baseThe rewritten branch descends from the intended updated main commit.
Recover the original history
If the result is wrong, the named backup is the clearest recovery point. Reflog also records recent ref movement for a bounded local period.
git reflog --date=iso feature/search
git show backup/feature-search-before-rebase
# Preserve a discovered old tip before further work
git branch recovery/feature-search <old-tip>
# In a disposable practice branch, restore exactly:
git reset --hard backup/feature-search-before-rebasePublish a rewrite only under an explicit agreement
If the branch has never been pushed, a normal push publishes the rewritten series. If a remote branch already contains the old series, replacing it is a coordination event—not routine cleanup.
git fetch origin
git log --oneline --left-right origin/feature/search...feature/search
# Only after confirming ownership and team agreement:
git push --force-with-lease origin feature/search--force-with-lease checks that the remote ref still matches your expected remote-tracking value. It can prevent overwriting unseen remote work, but it cannot repair downstream clones or substitute for communication.
Guided practice: update and clean a private branch
- 01Create divergence
In a disposable repository, make two main commits and four feature commits with one fixup-worthy change.
- 02Record evidence
Capture graph, unique ranges, tests, old tip, and a named backup branch.
- 03Rebase onto main
Resolve one intentional conflict, inspect the stopped commit, stage the result, and continue.
- 04Edit the series
Reword one message, fixup one correction, and split one mixed commit into coherent tested commits.
- 05Prove the result
Use range-diff, graph, tree diffs, tests, and clean status before deleting nothing.
Independent lab: rewrite, abort, recover, and publish safely
- Create a disposable bare remote and two clones representing you and a teammate.
- Build a five-commit private feature series containing a typo fix, a mixed commit, and two commits that should reverse order.
- Rebase onto updated main, deliberately abort the first attempt, prove the original tip returned, then rerun.
- Use interactive rebase to reword, reorder, autosquash, and split while preserving a named backup.
- Use
git range-diff, final tree comparison, tests, and graph evidence to account for every patch. - Publish the branch normally. Then simulate a coordinated rewrite and demonstrate that stale lease information prevents overwriting a teammate’s new remote commit.
- Recover the pre-rewrite series under a new branch using the backup or reflog, without deleting the verified rewritten branch.
Common rebase mistakes
Rebasing shared commits
Replacement IDs force collaborators to reconcile history they already consumed.
Running pull --rebase blindly
Know which local commits will replay onto which fetched upstream before accepting automation.
Skipping a conflict reflexively
Skip discards the current replayed patch. Prove redundancy before using it.
Trusting ours/theirs labels
Rebase side roles differ from normal merge intuition. Inspect stages and commits.
Force pushing without fetching
Stale remote knowledge weakens the lease and risks replacing someone else’s work.
Verifying only final files
A plausible tip can hide a dropped patch, broken intermediate commit, or misleading history.
Lesson review
Rebase is valuable when commit ownership is private and the rewrite has a clear purpose. You can now replay and edit a series while preserving recovery points, resolving operation-specific conflicts, comparing patch series, and refusing rewrites that would destabilize shared history.
- I explain why rebased commits receive new IDs.
- I inspect ownership, cleanliness, remote state, and the exact commit range before rewriting.
- I use interactive commands with oldest-first replay semantics.
- I verify rewritten series with range-diff, tree diffs, tests, and graph evidence.
- I use recovery refs and reserve force-with-lease for explicitly coordinated branch replacement.