| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | git 2.52.0 |
| Applies to | Any host with Git installed |
| Privilege | Normal user |
| Scope | Compare merge and rebase on the same diverged branch split, read the resulting history graph, choose merge vs rebase vs fast-forward, and contrast git pull with merge vs git pull --rebase. Not a full merge-conflict or interactive rebase tutorial. |
| Related guides | Git merge examples Git rebase tutorial Git pull command examples Git branch examples Git workflow |
git merge and git rebase both bring work from one branch into another — but they change history differently. Merge keeps the branch split visible and adds a merge commit when branches diverged. Rebase replays your commits on top of a newer base so the log reads like a straight line.
Four decisions this article covers:
- read the same branch split after merge vs after rebase;
- pick merge, rebase, or fast-forward for your situation;
- integrate a feature branch with merge or rebase-then-merge;
- choose
git pullvsgit pull --rebasewhen origin moved.
Pick your question in the table below, then jump to that section.
Merge vs Rebase at a Glance
Assume main gained commit E while feature added C and D from the older tip:
Before integration:
A---B---E main
\
C---D featureMerge on main |
Rebase feature onto main |
|
|---|---|---|
| History | Both lines stay visible; merge commit joins them | C and D become new commits after E |
| Commit hashes | Existing commits unchanged | Replayed commits get new hashes |
| Best for | Shared branches, release merges, preserving branch topology | Private feature branch cleanup before merge |
| Risk on shared branches | Low — no rewrite | High — others may have based work on old hashes |
For command-level merge and rebase walkthroughs, see Git merge examples and Git rebase tutorial.
Choose Merge or Rebase
| Your situation | Prefer |
|---|---|
| Integrate a shared or release branch | git merge |
| Refresh a local feature branch before opening a PR | git rebase main on the feature branch |
main has not diverged — feature is a straight extension |
Fast-forward merge (still a merge, no merge commit) |
| You already pushed the feature branch only you use | Rebase, then force-with-lease push — see Git rebase tutorial |
| Teammates based work on your branch commits | Merge — do not rebase those commits |
| Pull when origin moved and you have local commits | Team policy: git pull --no-rebase or git pull --rebase |
Neither command is universally better. The choice is whether you need preserved branch structure or a linear log — and whether rewriting commits would break someone else's clone.
Same Split, Merge Result
Scenario: main and feature both moved forward from B. You are on main and want feature's work integrated.
Merge combines the branches and records how they joined:
git merge featureMerge made by the 'ort' strategy.
c.txt | 1 +
d.txt | 1 +
2 files changed, 2 insertions(+)
create mode 100644 c.txt
create mode 100644 d.txtThe graph shows both lines plus a merge commit at the tip of main:
git log --oneline --graph --decorate -6* be32cd9 (HEAD -> main) Merge branch 'feature'
|\
| * 9cc8f96 (feature) D
| * cbec3c8 C
* | d88184e E
|/
* fddf8fe B
* 4a96b36 AThe merge commit (be32cd9) is the integration point. Every original commit hash is still there — useful when you need to see exactly when feature diverged and merged.
Same Split, Rebase Result
Scenario: the same starting split, but you want feature replayed on top of today's main before merging.
Switch to the feature branch and rebase onto main:
git switch featureReplay the feature commits on top of the current main tip:
git rebase mainRebasing (1/2)
Rebasing (2/2)
Successfully rebased and updated refs/heads/feature.The feature branch now looks linear — new commits sit after E:
git log --oneline --graph --decorate -6* 9dca12e (HEAD -> feature) D
* e9ac229 C
* d88184e (main) E
* fddf8fe B
* 4a96b36 Amain itself did not move during the rebase. Commits A, B, and E keep the same hashes — only the replayed feature commits changed (cbec3c8/9cc8f96 became e9ac229/9dca12e). From here many teams fast-forward main to feature or open a merge request with a single-line history.
Fast-Forward Merge
Scenario: feature branched from main, main did not gain new commits, and you merge feature into main.
When main is a direct ancestor of feature, Git can move the pointer without a merge commit:
git merge featureUpdating 93dd446..b374167
Fast-forward
c.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 c.txtConfirm the pointer moved with no merge commit in the graph:
git log --oneline --graph --decorate -4* b374167 (HEAD -> main, feature) C
* 93dd446 B
* 27df931 ANo merge commit appears — main simply catches up to feature. A rebase onto an unchanged main would also look linear, but fast-forward merge does not rewrite commit hashes.
Integrate a Feature Branch
Two common team patterns after main moved while you worked on feature.
Merge workflow — one merge commit on main, full branch visible:
git switch mainBring feature into main with a merge commit:
git merge featureRebase-then-integrate workflow — linear feature history first, then bring it into main:
git switch featureReplay feature onto the latest main:
git rebase mainReturn to main once the rebase finishes:
git switch mainIntegrate the rebased feature — often a fast-forward:
git merge featureAfter a successful rebase, that final merge is often a fast-forward. The difference is whether intermediate history shows the old branch split (merge-only) or a straight line (rebase first). For conflict handling during either path, see Git merge examples and Git rebase tutorial.
git pull With Merge vs --rebase
Scenario: you have a local commit, origin/main moved, and you need to integrate the fetched updates.
git pull --no-rebase tells Git to integrate the fetched branch with a merge — when histories diverged, that produces a merge commit:
git pull --no-rebaseMerge made by the 'ort' strategy.
remote.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 remote.txtThe merge pull leaves a join commit in the graph:
git log --oneline --graph --decorate -5* ac6a640 (HEAD -> main) Merge branch 'main' of origin
|\
| * 5c741c7 (origin/main) Remote commit on origin
* | 3a46547 Local commit before pull
|/
* be2104a Shared base on origingit pull --rebase replays your local commits on top of the fetched tip instead:
git pull --rebaseRebasing (1/1)
Successfully rebased and updated refs/heads/main.After rebase pull, the log is linear with your local commit on top:
git log --oneline --graph --decorate -5* a227dbb (HEAD -> main) Local commit before pull
* 147691f (origin/main) Remote commit on origin
* 61920a1 Shared base on originShort status after rebase pull shows one commit ahead — no merge commit:
git status -sb## main...origin/main [ahead 1]You can configure pull.rebase true if your team prefers rebase-based pulls, or use git pull --no-rebase when you explicitly want merge-based integration. Match your team's policy before rebasing commits others might already have.
When Not to Rebase
Rebase rewrites commits. Avoid it when:
- others already pulled the commits you would replay;
- the commits are on a shared integration branch and others may already have based work on them;
- you are unsure whether anyone based work on your SHAs.
Merge is the safer default for those cases because it adds a merge commit without changing existing hashes. If you already rebased a pushed branch by mistake, recovery starts with git reflog — see Git reflog tutorial.
git reset is a different tool entirely — it moves a branch pointer rather than replaying commits. See Git reset examples when you need to undo local commits, not integrate branches.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
git rebase stops with conflicts |
Diverged changes in the same lines | Fix files, git add, git rebase --continue — or git rebase --abort |
| Teammate's branch breaks after your push | You rebased commits they already had | Avoid rebasing shared history; coordinate force-with-lease only on private branches |
| History looks tangled after many merges | Normal merge workflow on busy main |
Expected with merge; use rebase on local features before integrating if team allows |
| Expected fast-forward but got merge commit | main diverged from feature |
A fast-forward is no longer possible because both branches contain unique commits. Merge them as-is, or rebase feature onto main first if your workflow requires linear history |
git pull --no-rebase created a merge commit you did not want |
Pull was configured or requested to merge | Use git pull --rebase if team policy allows |
| Merge or rebase stopped and you are not sure which operation is active | An unfinished merge or rebase is still open | Run git status; Git reports whether a merge or rebase is currently in progress — see unmerged files pull error |
References
Summary
git merge and git rebase solve the same problem — bringing branch work together — with different history shapes. On a diverged split, merge keeps both lines and adds a merge commit; rebase replays your commits after the newer base with new hashes.
Fast-forward merge is the simple case where main can move forward without a merge commit. Many feature workflows rebase onto main first for a linear log, then merge or fast-forward into main. When local and remote diverged, git pull --no-rebase integrates with a merge commit; git pull --rebase replays your local commits on top of the fetched tip instead.
Choose merge when the branch timeline must stay intact or the branch is shared. Choose rebase on local feature work you have not shared — or when your team standardizes on rebase pulls — and avoid rewriting commits others already built on. Step-by-step merge and rebase commands live in Git merge examples and Git rebase tutorial.

