Merge vs Rebase
Merge joins histories. Rebase recreates commits on a different base.
Introduction
git merge and git rebase can bring a feature branch up to date or prepare it for integration. Merge preserves the existing commits and records how two histories joined. Rebase replaces one line of commits so it begins at a different point.
The choice affects history and collaboration. It does not change whether the final files can be correct.
Understanding the Concept
A merge commit has two or more parents. For a typical two-branch merge, the first parent is the previous tip of the current branch and the second parent is the tip being merged. Existing commits keep their object IDs.
Definition
Merge Commit
A commit with two or more parents that records the point where separate lines of history were combined.
A rebase computes patches from commits on the current branch, applies them to a new base in order, and moves the branch ref to the new commits. Their object IDs change because their parent history changes.
Definition
Fast-forward
A ref update that moves a branch pointer to a descendant commit without creating a merge commit.
| Merge | Rebase | |
|---|---|---|
| Existing commits | Preserved | Replayed commits are replaced |
| History | Retains branch topology | Produces a linearized branch |
| Collaboration | Safe for shared history | Requires coordination after sharing |
| Conflicts | Usually resolved in one merge result | Can recur as each commit is replayed |
| Integration record | Merge commit when not fast-forwarded | No integration commit from rebase itself |
A merge may fast-forward when the current branch is an ancestor of the branch being merged. In that case Git moves the branch ref and creates no merge commit. Use --no-ff when the project requires an explicit integration commit.
Applying It in Practice
Merge main into a shared feature branch without rewriting its commits:
git fetch origin
git switch feature/my-work
git merge origin/main
Rebase a private feature branch onto current main:
git fetch origin
git switch feature/my-work
git rebase origin/main
If that branch already exists on a remote and its rewrite is agreed, update it with:
git push --force-with-lease origin feature/my-work
Integrate a branch with an explicit merge commit:
git switch main
git merge --no-ff feature/my-work
A squash merge creates one new commit containing the branch's combined file changes. It does not create a merge commit, so Git does not record the feature branch as a parent:
git switch main
git merge --squash feature/my-work
git commit -m "Add user authentication"
Hosting platforms also offer rebase-and-merge. That option replays pull-request commits onto the target branch and usually creates new object IDs. Check the platform's exact behavior before making policy depend on it.
Engineering Considerations
Merge is the conservative choice for shared branches because it does not replace commits. The graph records both the development history and the integration event. First-parent logs can show the sequence of changes merged into main without expanding every side branch:
git log --first-parent --oneline main
Rebase is useful for reorganizing private work and for moving a branch onto its intended base. A linear history can be easier to scan, but it removes the original branch topology. Do not rewrite commits that colleagues have based work on unless everyone coordinates the recovery.
git bisect works on both linear and merged histories. Merge commits can make a regression harder to interpret when neither parent alone reproduces the merged result, while a squash can hide which internal step introduced the bug. History quality depends more on buildable, focused commits than on graph shape alone.
git blame also works across merges. Options such as -M and -C help detect moved or copied lines. Rebase does not automatically make blame more accurate.
Scaling and Operations
Set one integration policy for each protected branch. Common choices are:
- merge commits for an explicit first-parent release history
- squash merges for one target-branch commit per pull request
- rebase-and-merge for preserving individual pull-request commits in a linear history
Each choice discards different information. Merge commits retain topology. Squash merges retain the final combined change but not the branch's commit boundaries. Rebase-and-merge retains boundaries but replaces commit identities.
Protect shared branches from force pushes. If feature branches are rewriteable, make that policy explicit so reviewers know whether to build dependent work on them.
Use git range-diff after a substantial rebase to verify that the rewritten series still represents the intended patches. Run the full test suite after conflict resolution, regardless of strategy.
Next Steps
- Cherry-pick vs Rebase: distinguish selected patch copying from branch rewriting
- What are Stacked PRs?: manage branches that depend on one another
- Stacked PR Workflow: apply rebasing to a pull-request stack
- Parallel Development Workflow: combine integration policy with concurrent branch work