When we rebase our feature branches, we create a clean, linear git history that's easier to read and debug. Instead of messy merge commits cluttering our timeline, each feature appears as a clean sequence of commits on top of the main development line.
Benefits:
git bisectDON'T DO THIS:
git checkout <feature-branch>
git merge dev # ❌ This creates merge commitsWhy this is problematic:
The visual difference:
Bad approach (merge dev into feature):
dev: A - B - C - D - E
\ \
feature: F - G - M - HM is a merge commit that clutters history.
Good approach (rebase feature onto dev):
dev: A - B - C - D - E
\
feature: F' - G' - H'Clean, linear history where feature commits appear after dev commits.
Instead of merging dev into your feature branch, rebase regularly:
# Do this every few days or when you need latest changes
git checkout dev
git pull origin dev
git checkout <feature-branch>
git rebase dev
git push --force-with-lease# When you want to clean up your commits AND get latest changes
git checkout dev
git pull origin dev
git checkout <feature-branch>
git rebase -i dev # Opens editor to squash/reorder commits
git push --force-with-leaseBenefits of regular rebasing:
Here's the step-by-step process to rebase your feature branch before merging:
# Make sure you're on your feature branch
git checkout <feature-branch>
# Pull any recent changes from the remote feature branch
git pull# Switch to dev branch
git checkout dev
# Pull the latest changes
git pull origin dev# Switch back to your feature branch
git checkout <feature-branch>
# Rebase on top of the latest dev changes
git rebase dev# Force push your rebased changes
git push --force-with-leaseNote: We use --force-with-lease instead of --force as it's safer - it will fail if someone else has pushed changes to your branch that you don't have locally.
Sometimes you'll encounter conflicts during the rebase. Here's how to handle them:
# When conflicts occur during rebase
# 1. Fix conflicts in your editor
# 2. Stage the resolved files
git add <resolved-files>
# 3. Continue the rebase
git rebase --continue
# If you want to abort and start over
git rebase --abortFor experienced users, here's the quick sequence:
git checkout <feature-branch>
git pull
git checkout dev
git pull origin dev
git checkout <feature-branch>
git rebase dev
git push --force-with-lease
# Then merge via GitHub UIgit pull --rebaseIn our workflow above, we could simplify Step 1 by using:
git checkout <feature-branch>
git pull --rebase origin <feature-branch>What git pull --rebase does:
git pull = git fetch + git mergegit pull --rebase = git fetch + git rebaseWhen to use it:
In our workflow: You could use it in Step 1, but regular git pull is fine since we're rebasing the entire branch onto dev anyway.
--force-with-lease--force-with-lease is a safer version of --force that includes a safety check:
What --force does:
git push --force # "Overwrite remote branch, I don't care what's there"What --force-with-lease does:
git push --force-with-lease # "Overwrite remote branch, BUT only if it matches what I last saw"The safety mechanism:
--force-with-lease will failExample scenario:
git push --force would destroy their fixgit push --force-with-lease would fail and warn youWhen it fails:
git push --force-with-lease
# Error: Updates were rejected because the remote contains work that you do not have locallyThen you'd need to:
git pull --rebase # Get teammate's changes
git push --force-with-lease # Now it worksFor your first few PRs: Try this workflow on smaller, less critical features to get comfortable with it.
If you're nervous about force pushing: Create a backup branch first:
git checkout <feature-branch>
git checkout -b <feature-branch>-backup
git checkout <feature-branch>
# Now proceed with rebase workflowIf you accidentally merged dev into your feature branch:
git checkout <feature-branch>
git reset --hard <last-feature-commit-before-merge>
git rebase devgit checkout <feature-branch>
git checkout -b <feature-branch>-clean
git reset --hard <last-feature-commit-before-merge>
git rebase dev
git push --force-with-lease origin <feature-branch>-clean
# Update your PR to point to the new branch"I have merge commits in my feature branch"
git rebase dev instead of git merge dev"Rebase conflicts are overwhelming"
git rebase --abort to cancel and ask for helpgit rebase -i to squash commits first"I'm scared of force pushing"
--force-with-lease instead of --forceIf you run into issues or have questions about this workflow, feel free to ask! We can pair program through your first few rebases until you're comfortable.
Reference: Detailed Stack Overflow explanation