Interactive Rebase Workflows Jump to heading
Interactive rebase gives you direct, surgical control over commit history before it lands on a shared branch. As part of Conflict Resolution & Safe Merge Operations, this workflow sits between raw feature development and final integration: you linearize commits, fix messages, collapse WIP noise, and eliminate merge artifacts before reviewers ever see the diff.
Prerequisites Jump to heading
Before starting any interactive rebase session, confirm:
Step 1 β Configure the Environment Jump to heading
Intent: Enable the Git settings that make interactive rebase safer and less repetitive.
# Cache conflict resolutions; replays them if the same hunk recurs
git config --global rerere.enabled true
# Auto-order --fixup commits above their targets when rebasing
git config --global rebase.autoSquash true
# Move sibling branch pointers when rebasing stacked branches (Git 2.38+)
git config --global rebase.updateRefs true Verification:
git config --global --list | grep -E "rerere|rebase"
# Expected output:
# rerere.enabled=true
# rebase.autosquash=true
# rebase.updaterefs=true Step 2 β Open the Interactive Todo List Jump to heading
Intent: Identify the commits to rewrite and choose an action for each one.
# Rebase every commit since main that is not yet merged
git rebase -i main
# Alternative: rebase the last N commits regardless of branch
git rebase -i HEAD~5 Git opens your $EDITOR with one line per commit, oldest first:
pick a1b2c3d feat: add payment gateway integration
pick e4f5g6h fix: handle nil user in checkout
pick i7j8k9l wip: debug logging
pick m0n1o2p chore: remove debug logging
Available action verbs:
| Action | Effect |
|---|---|
pick | Keep the commit exactly as-is |
reword | Keep the commit but open the editor to change its message |
edit | Pause after applying; use git commit --amend or split the commit |
squash | Merge into the commit above; opens editor to combine messages |
fixup | Merge into the commit above; silently discards this message |
drop | Remove the commit entirely from the branch |
Verification: After saving and closing the editor, run:
git log --oneline main..HEAD
# Confirm the commit list matches your intended shape Step 3 β Use --fixup and --autosquash for Automated Compaction Jump to heading
Intent: Mark correction commits at write-time so --autosquash collapses them automatically, avoiding manual todo-list editing.
# While working: create a fixup targeting a specific earlier commit
git commit --fixup a1b2c3d # message becomes "fixup! feat: add payment gateway integration"
# Rebase: --autosquash reorders and collapses fixup commits automatically
git rebase -i --autosquash main With rebase.autoSquash=true in your config, omit the flag:
git rebase -i main # --autosquash applied automatically Verification:
git log --oneline main..HEAD
# The fixup commit should no longer appear as a separate entry SAFETY WARNING:
--autosquashsilently reorders commits. If a fixup target SHA no longer exists (because a prior rebase already squashed it), Git will error. Always checkgit log --onelinebefore starting.
Step 4 β Resolve Conflicts During the Rebase Loop Jump to heading
Intent: Unblock a halted rebase when an individual commitβs patch conflicts with the current branch state.
When a conflict stops the rebase, Git prints the conflicting paths and waits:
# Check what is conflicting
git status
# Look for "both modified" entries
# Open the file in your editor, resolve the conflict markers, then stage
git add src/payments/gateway.py
# Resume applying the remaining commits
git rebase --continue If rerere has seen this conflict before, it replays the cached resolution automatically β you still need to verify and run git add before --continue.
To cancel and restore your branch to its pre-rebase state:
git rebase --abort Verification: After each --continue, run git diff HEAD~1 HEAD to confirm the resulting commit contains only the intended change.
SAFETY WARNING: Never edit files directly inside
.git/rebase-merge/. Always usegit add+git rebase --continueto maintain index integrity. Editing those internal files corrupts the rebase state and may requiregit rebase --abortto recover.
Step 5 β Force-Push the Rewritten Branch Jump to heading
Intent: Publish the rewritten history to the remote without overwriting work pushed by others since your last fetch.
git push --force-with-lease origin feat/payment-gateway --force-with-lease checks that the remote ref still matches the SHA you last fetched. If another engineer pushed to the same branch while you were rebasing, the push is rejected and no work is lost. Bare --force skips this check and must not be used on shared branches.
For branches with pre-push validation rules configured, the hook runs before the push. If it fails, fix the issue β do not bypass hooks with --no-verify.
Verification:
git log --oneline origin/feat/payment-gateway..feat/payment-gateway
# Should output nothing β local and remote are in sync SAFETY WARNING: Force-pushing to a branch that other engineers have based work on rewrites their merge bases and forces them to re-sync manually. Confirm isolation or coordinate a push window before proceeding.
Integration with Adjacent Workflows Jump to heading
3-Way Merge Fundamentals Jump to heading
When the underlying goal is integration rather than history cleanup, 3-way merge fundamentals produce a non-destructive record of the merge event. The boundary of responsibility is clear: use interactive rebase to clean up a feature branch before opening a pull request; use merge to record the integration event on the mainline. The two are complementary, not competing.
Squash and Fixup Strategies Jump to heading
Squash and fixup strategies extend the squash/fixup actions into a team-wide convention, covering when to squash on merge via the GitHub UI versus during the rebase itself. The key distinction: squashing during rebase preserves the logical commit boundary on the feature branch for local review; squashing on merge collapses everything to a single commit on main. Many teams combine both: clean up within the feature branch via interactive rebase, then allow the merge queue to squash to one commit on mainline.
CI/CD Automation Jump to heading
After a force-push, any CI/CD pipeline trigger mapping that caches artifacts by commit SHA must invalidate those caches. Configure path-aware triggers so that only the changed sub-trees re-run expensive jobs. Pre-rebase hooks β managed through local hook configuration with Husky β can validate commit message conventions and run fast linters before the operation starts, embedding quality gates before history is rewritten.
Troubleshooting Jump to heading
| Symptom | Likely Cause | Fix |
|---|---|---|
fatal: invalid upstream 'main' | Branch name mismatch or missing remote ref | Run git branch -a to confirm the base name; use origin/main if the local tracking branch is absent |
| Rebase stops mid-way with no conflict shown | edit action pauses intentionally | Amend the commit with git commit --amend, then git rebase --continue |
--force-with-lease push rejected | Remote was updated after your last fetch | Run git fetch origin, inspect the new commits, and re-run the rebase on top of the updated remote |
rerere replays a wrong resolution | Cached resolution is stale or was recorded on an unrelated conflict | Clear the cache with git rerere forget <path> and re-resolve manually |
| GPG signatures missing after rebase | Rebase rewrites SHAs, invalidating signatures | Add --gpg-sign to the rebase command or set commit.gpgSign=true globally |
| Stacked branch pointer left behind | rebase.updateRefs not enabled | Enable with git config --global rebase.updateRefs true (Git 2.38+) and re-run |
Recovery Procedures Jump to heading
If a rebase is interrupted or produces wrong results, git reflog provides a full chronological record of every HEAD movement:
git reflog | head -20
# Look for the entry immediately before "rebase (start)" Restore from that SHA:
git reset --hard <sha-before-rebase> ORIG_HEAD also points to the pre-rebase HEAD immediately after the operation completes:
git reset --hard ORIG_HEAD SAFETY WARNING:
git reset --harddiscards all uncommitted changes in the working tree. Stash or commit anything you need before running it.
For the full coordination protocol on shared branches β including reflog retention policies, GPG verification, and DCO compliance after rewrites β see Safe git rebase -i for shared branches.
Frequently Asked Questions Jump to heading
Is it safe to rebase a branch that is already open as a pull request? Jump to heading
It is safe only if you are the sole contributor and you notify any reviewers before force-pushing. GitHub and GitLab preserve the review thread but mark existing diff comments as outdated after the force-push. Always use --force-with-lease. If the PR is under active review with multiple participants, consider completing the review cycle first, then rebasing before final merge.
What is the difference between squash and fixup in git rebase -i? Jump to heading
Both merge the marked commit into the one above it. squash opens the editor so you can combine or edit the resulting message. fixup silently discards the child commit message and keeps only the parent message, making it the right choice for automated --autosquash workflows where the correction commit is noise, not content.
How do I recover from a rebase that went wrong? Jump to heading
Run git reflog to find the SHA labelled rebase (start) and restore it with git reset --hard <sha>. Alternatively, git reset --hard ORIG_HEAD works immediately after most rebase operations complete. If the rebase is still in progress, use git rebase --abort to cancel it cleanly.
Can I rebase and keep GPG commit signatures intact? Jump to heading
No. Rebasing rewrites SHA hashes, which invalidates existing GPG signatures. Pass --gpg-sign to the rebase command, or set commit.gpgSign=true globally, to re-sign each replayed commit.
What does rebase.updateRefs do? Jump to heading
When you maintain stacked branches β branch B based on branch A, branch C based on B β rebase.updateRefs=true automatically moves the sibling branch pointers as each branch is rebased, preventing them from being left pointing at orphaned commits. Available in Git 2.38 and later.
Related Jump to heading
- Safe git rebase -i for shared branches β detailed coordination protocol for rebasing branches that multiple engineers have already fetched
- Squash and Fixup Strategies β team conventions for when to squash during rebase versus on merge, and how
--fixupcommits fit into a daily workflow - 3-Way Merge Fundamentals β when to choose a merge commit over a rebase to preserve integration topology
- Cherry-Pick and Backporting β selectively applying commits across branches when a full rebase is inappropriate
- Pre-Push Validation Rules β hook-based gates that run before
git push --force-with-leaseto catch issues before rewritten history reaches the remote