Git Merge vs Rebase — A Calculator App Case Study

Chandra SekharChandra Sekhar
4 min read
  • Merge preserves history and creates a merge commit. Great for shared branches (like a team feature branch) and audit trails.

  • Rebase rewrites history to make it linear, as if your work started from the latest main. Great for your own local branches before you publish them.

  • With 5 developers pushing to the same feature branch, prefer merging main into that shared branch. Use rebase for individual dev branches before they push.


The scenario

You’re building a Calculator application.

  • main already has 100 commits.

  • You created a feature branch: feature/complex-cal, with 5 developers collaborating on it. They make around 100 commits on this branch.

  • While they’re working, main moves forward with another 75 commits.

Now you need to bring the latest from main into feature/complex-cal so you can integrate safely and resolve conflicts early. Do you merge or rebase?


What merge does (no history rewrite)

Merge takes two histories and ties them together with a merge commit. Your feature branch’s existing commits stay exactly as they were; Git just creates a new commit connecting both histories.

# from inside feature branch
git checkout feature/complex-cal
git fetch origin
git merge origin/main

Why teams like it

  • No history rewrite → safe for shared branches with many developers.

  • Clear audit trail: you can see when you integrated main.

  • Conflict resolution happens once in the merge commit, visible to all.

How it looks:

gitGraph
  commit id: "M1" tag: "main@100"
  branch feature/complex-cal
  checkout feature/complex-cal
  commit id: "F1"
  commit id: "F2"
  checkout main
  commit id: "M2"
  commit id: "M3" tag: "main@175"
  checkout feature/complex-cal
  merge main tag: "Merge main->feature"
  commit id: "F3"

What rebase does (history rewrite → linear)

Rebase takes your feature branch commits and replays them on top of the latest main tip, creating new commit IDs. The result is a linear history as if you started from the newest main.

# from inside your feature branch
git checkout feature/complex-cal
git fetch origin
git rebase origin/main

If conflicts happen:

git add <files>
git rebase --continue
# or
# git rebase --skip
# git rebase --abort

After rebase, force-push safely:

git push --force-with-lease

How it looks (after rebase):

gitGraph
  commit id: "M1" tag: "main@100"
  commit id: "M2"
  commit id: "M3" tag: "main@175"
  branch feature/complex-cal
  checkout feature/complex-cal
  commit id: "F1p"
  commit id: "F2p"

Key differences

  • History shape: Merge → branching graph. Rebase → straight line.

  • Safety: Merge → safe for shared branches. Rebase → risky on shared branches.

  • Commit IDs: Merge → preserved. Rebase → rewritten.

  • Conflicts: Merge → once, at merge. Rebase → per commit during replay.

  • Logs: Merge → branchy. Rebase → clean linear.


Which one should you use?

  • You have 5 developers pushing to the same feature/complex-cal. That’s a shared branch.

  • main moved ahead by 75 commits while feature added ~100 commits.

Recommended:

  • Use merge regularly to bring main into the shared feature branch.

  • Developers can rebase their personal work branches before pushing.

  • When the feature is done, merge it back into main (with --no-ff if you want to keep the boundary visible).


Flowcharts

1) git fetch workflow

flowchart LR
  subgraph FETCH [git fetch Workflow]
    F1([Run: git fetch]) --> F2[Contact remote repo]
    F2 --> F3[Download latest commits and branches]
    F3 --> F4[Update remote-tracking refs: origin/main]
    F4 --> F5[Local branch stays the same]
    F5 --> F6[You decide: merge or rebase manually]
  end

2) Sync with merge

flowchart LR
  subgraph MERGE [Sync feature with main via MERGE]
    A1([Checkout feature/complex-cal]) --> A2[git fetch origin]
    A2 --> A3[git merge origin/main]
    A3 --> A4{Conflicts?}
    A4 -- No --> A5[Merge commit created]
    A4 -- Yes --> A6[Resolve conflicts]
    A6 --> A7[git add .]
    A7 --> A8[git commit]
    A8 --> A5
    A5 --> A9[Commit IDs preserved]
    A9 --> A10[Push: git push]
  end

3) Sync with rebase

flowchart LR
  subgraph REBASE [Sync feature with main via REBASE]
    B1([Checkout feature/complex-cal]) --> B2[git fetch origin]
    B2 --> B3[git rebase origin/main]
    B3 --> B4{Conflicts?}
    B4 -- No --> B8[Linear history created]
    B4 -- Yes --> B5[Resolve conflicts per commit]
    B5 --> B6[git add <files>]
    B6 --> B7[git rebase --continue]
    B7 --> B4
    B8 --> B9[Force push safely]
  end

Bottom line

  • Use merge for shared branches (your team’s feature/complex-cal).

  • Use rebase on personal/local branches before pushing.

  • Both are correct; choose based on team workflow and safety.


0
Subscribe to my newsletter

Read articles from Chandra Sekhar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chandra Sekhar
Chandra Sekhar