Skip to content
Field Notes
Go back

Git Workflow: Merge vs Rebase for Feature Branches

When your feature branch falls behind main, you have two options to sync it up: merge or rebase. Both achieve the same goal, your branch includes the latest changes from main, but they work differently and have different consequences for your PR, code review, and git history.

Let’s break down the trade-offs.


TL;DR

If your team squash-merges PRs into main, rebasing feature branches onto main before opening a PR produces cleaner diffs and better code reviews, with no downsides for solo feature branches. Using merge syncs the branch but adds unrelated CICD commits to your PR, creating messier diffs and requiring reviewers to filter out noise.


The Two Flows

Merge flow

main:    A---B---C---D(CICD)---E
              \           \
feature:        F---G---M(merge)---H---I

Running git merge main when the branch falls behind creates merge commits inside the feature branch. CICD commits from main appear as part of the PR’s proposed changes.

Rebase flow

main:    A---B---C---D---E
                         \
feature:                   F'---G'---H'

Running git rebase main instead replays feature commits on top of latest main. SHAs change (F→F’), requires force-push, and is safe for solo feature branches.


The “squash anyway” argument

A common reasoning goes: “We squash-merge everything so main stays clean. The spaghetti only lives in the PR branch temporarily.”

The key consideration: code review happens before the squash.

ProblemMerge flowRebase flow
PR diff includes main commitsYes — reviewers see CICD/infra noiseNo — only feature changes
git log on branchTangled with merge commitsLinear, only feature work
GitHub “Files changed” tabCan show unrelated filesClean
Conflict resolutionAll at once in one merge commitOne commit at a time, in context
CICD commits in PRAppear as part of proposed changesInvisible (they’re the base)

The squash cleans main’s history, but review quality degrades before that happens.


The CICD commit problem specifically

Most concrete symptom of the merge flow:

git merge main   # pulls in CICD commit locally
git push         # PR now shows CICD commit as "your changes"

Reviewers must mentally filter: “Is this intentional or noise from main?” With rebase, CICD commits are part of the base — completely invisible in the diff.


Real impacts in this specific workflow

Things genuinely affected by using merge:

Things genuinely not affected (squash handles these):


Strongest argument for keeping the merge flow

Simplicity. Rebase requires devs to understand:

  1. SHAs change after rebase → force-push is required
  2. Force-pushing a branch someone else has checked out destroys their work
  3. If you have commits on top of an in-flight rebase, they can be lost

For solo feature branches (one dev per branch, which is the standard PR model), these risks are absent. The danger only applies to shared branches.


Verdict

The rebase approach has meaningful advantages in this workflow because:

  1. Solo feature branches — the main risk of rebase (force-push on a shared branch) doesn’t exist
  2. Squash+merge final strategy — no “rewritten history in main” concern
  3. Review quality is real — the only window to catch bugs is the PR review
  4. The CICD noise problem is a direct, preventable consequence of the merge flow

Practical adoption path

Lowest friction first:

git config --global pull.rebase true

This alone prevents the accidental merge commits that come from git pull (the most common cause of the problem, even for devs who try to rebase).

Branch protection (GitHub/GitLab): Require branches to be up-to-date before merge. This forces the question of how to sync — good moment to standardize on rebase.

Reduce branch lifetime: The longer a branch lives, the more painful any sync strategy becomes. Smaller PRs reduce the need to sync with main at all — the root cause fix.


References


Share this post on: