Skip to main content
Back to Blog

Git rebase is always the wrong choice.

Please, stop making everyone suffer.

4 min read

Let me just rebase this real quick

Git rebase is often sold as a way to maintain a “clean” history. The claim is that a linear sequence of commits is easier to read and understand. This is false.

When you rebase, you rewrite commit hashes. The original commits, their timestamps, context, and the order in which changes actually happened, are destroyed. History should be immutable. Rebase violates this principle, and every downstream tool that depends on accurate history suffers for it.

When you rebase a branch that others have based work on, you force them to deal with divergent histories. This means force pushes that overwrite teammates’ work, lost commits when people pull the rebased branch, and hours wasted resolving conflicts that should not exist.

With merge, you resolve conflicts once. With rebase, you resolve the same conflict multiple times, once for each commit being replayed.

Merge commits are informative. They show when features were integrated and by whom. Squash merge achieves linearity without rewriting history. A history that lies about what happened is worse than an honest one.

gitGraph
   commit id: "A1"
   commit id: "A2"
   branch feature
   checkout feature
   commit id: "B1"
   commit id: "B2"
   commit id: "B3"
   checkout main
   commit id: "A3"
   commit id: "A4"
   commit id: "A5"
   merge feature
   commit id: "A6"
gitGraph
   commit id: "A1"
   commit id: "A2"
   commit id: "A3"
   commit id: "A4"
   commit id: "A5"
   commit id: "B1"
   commit id: "B2"
   commit id: "B3"
   commit id: "A6"

In this visual, it is simple. Branch main is ahead of feature by 3 commits, and branch feature is ahead of main by 3 commits. In a scenario with no conflict, if you want to rebase your feature branch commits on top of main, you will need to force push. That’s already a red flag. Now imagine n commits with n conflicts. This should not happen in a communicative team, but it will happen when collaborators do not know each other, such as in open source. You will then need to resolve conflicts for every commit. That’s a ton of work for a ton of nothing. Zero productivity boost and zero gains from it.

The worst has yet to come, as you will likely squash your PR to main at some point, if you want the main history to show a list of features (rather than a list of useless commits). This means that a PR squash would equal an issue, itself being a new feature or a bug fix or whatever. That means all that extra work is actually thrown in the bin. That’s right. You resolved n conflicts but there will be only one commit anyway in main. And I know what a few people will think. What if we rebase then, the PR branch directly to main (thus keeping all the rewritten commits with a linear history). Fantastic idea! Actually that’s really a bad idea. You will rebase n commits from your branch, but you will merge your branch only assuming the latest commit compiles and passes tests. The conflicts you resolved represent states of the project that never existed and were never tested. You worked on a version A2 of main, added features, and somehow while resolving conflicts you are telling me that adding commits A3, A4 and A5 from main in between your code will work? Did you actually check? I know, you didn’t check, you won’t check, and CI/CD will not check every individual commit. You kept a linear history of garbage commits.

  • Rebasing the latest main into your feature branch is wasted effort if you later squash the PR — all that extra conflict resolution is thrown away. You took unnecessary risk for the same exact result.
  • Rebasing your feature branch on top of main rather than squashing adds untested commits on the main branch.

Keep exploring across the site

Use this post as a jumping-off point into the DSA atlas, the graphics atlas, or the dedicated discovery page when you want a more structured next step.

DSA Atlas

Linear toolkit

->

Learn how sequence-shaped data unlocks queues, stacks, windows, and fast pointer manipulations.

7 topics

Deque (Double-Ended Queue)Linked List OperationsQueue

DSA Atlas

Graph toolkit

->

Start with graph shape, then layer traversal, connectivity structure, and both single-source and all-pairs path algorithms.

15 topics

BFS — Breadth-First SearchDFS — Depth-First SearchGraph Fundamentals

System Design Atlas

Traffic control core

->

Start with bucket math, then move into rate limiting, reliability controls, feedback loops, and saturation management.

8 topics

Token Bucket, GCRA, and Virtual TimeDesigning a Rate Limiter (at Scale, Production-Grade)Global Quotas (Hierarchical Budgets Across Regions and Fleets)

System Design Atlas

Control loops and stability

->

Connect integrators, PI-style controllers, hysteresis, anti-windup, and oscillation detection to the distributed systems that use them.

6 topics

Token Bucket, GCRA, and Virtual TimeDesigning a Rate Limiter (at Scale, Production-Grade)Global Quotas (Hierarchical Budgets Across Regions and Fleets)

Graphics Atlas

Color and tone pipeline

->

Track how values move from color spaces and gamma into dynamic-range compression, equalization, dithering, and compositing.

7 topics

Alpha CompositingColor SpacesDithering

Graphics Atlas

Mask analysis and cleanup

->

Build a binary mask, explore connectivity, then repair or simplify it with morphology-aware tools.

9 topics

Image Processing FundamentalsFlood FillThresholding

Discovery

Search across the whole site

->

Jump from writing into the system design, DSA, graphics, demos, and other content when you want a faster way to connect the dots.

Unified search

Ctrl+KPagefindAtlases