What Does git rebase Actually Do?
A deep, intermediate-level walkthrough of how rebasing rewrites history, why teams use it deliberately, and where it can go wrong if handled carelessly.
Imagine you’re telling a story out loud, and partway through, you realize a small side comment you made a few sentences ago would actually flow better if it came right before the ending instead. You could either leave it exactly where it fell in the moment, or you could retell the whole story from that point onward, placing that comment exactly where it now makes the most sense. Rebasing in Git is that second option, applied to your project’s history. Instead of accepting commits in whatever order they happened to occur, rebasing lets you take a set of commits and replay them, one at a time, onto a new starting point — effectively rewriting the story of how your work came together, as if it had happened in a cleaner, more deliberate order from the very start. This post goes deep into what rebase actually does internally, why experienced teams reach for it, and the very real risks that come with rewriting something as fundamental as history.
1What Problem Does Rebase Solve?
To understand rebase, we first need to see the messy situation it was built to address.
The problem: history that reflects timing, not intent
When multiple people work on a shared project at the same time, their commits naturally interleave based on when each person happened to save their work — not based on what actually makes logical sense as a readable story. One developer’s three small commits fixing a typo might end up scattered awkwardly between another developer’s much larger feature work, purely because of when each commit happened to be made. Left alone, a project’s history can end up reflecting the chaos of real-time collaboration rather than a clean, understandable narrative of how the project actually evolved.
The solution: replaying commits onto a new base
Rebase addresses this by letting you take a sequence of commits and move them, as a whole, onto a different starting point — most often, the latest version of the branch you’re eventually planning to merge into. Rather than merging two diverging histories together as they are, rebase effectively asks: “what if these commits had been made starting from right here, instead of from wherever they originally started?” It then reconstructs that alternate reality, one commit at a time.
Picture building a small tower out of blocks on one table, while a friend builds a much taller tower on a second table nearby. If you wanted to combine your tower onto theirs, you have two choices. You could glue your entire tower, as a separate unit, onto the side of theirs — messy, but fast, and it visibly shows two towers joined together. Or, you could carefully pick up your blocks one at a time, and re-stack them directly on top of your friend’s tower, in the same order you originally placed them, as though you’d been building on their table all along. The second approach is what rebase does with commits — it picks them up one at a time and re-stacks them on a new foundation.
Commit
A single, permanent snapshot in a project’s history, with its own message and unique identifier.
Base
The commit a sequence of other commits is built on top of — the “starting point” for a branch’s history.
Rebase
The act of moving a sequence of commits so they now build on top of a different base commit.
History Rewriting
Changing the recorded sequence of commits, rather than simply adding new ones on top of what already exists.
2Merge vs Rebase: Two Different Ways to Combine Work
Rebase is almost always discussed alongside its closest relative, merging, since both exist to solve the same underlying goal in fundamentally different ways.
Merging takes two separate lines of history and combines them by creating a brand-new commit with two parents, one pointing back into each original line. Nothing about either original line of history is changed — the merge commit simply sits on top, tying them together. Rebasing, by contrast, doesn’t create a joining commit at all. Instead, it takes one line of commits and moves them entirely, rewriting them so they appear to have been built directly on top of the other line from the very beginning.
flowchart TD
subgraph Merge
A1["main"] --> A2["main"]
A2 --> A3["merge commit"]
B1["feature"] --> B2["feature"]
B2 --> A3
end
subgraph Rebase
C1["main"] --> C2["main"]
C2 --> D1["feature (replayed)"]
D1 --> D2["feature (replayed)"]
end
| Aspect | Merge | Rebase |
|---|---|---|
| Original commits | Left completely unchanged | Rewritten as new, different commits |
| Resulting history shape | Branching, with joining points visible | A single, straight line |
| Extra commit created | Yes, a dedicated merge commit | No new joining commit |
| Safety on shared branches | Generally safe for everyone | Risky if others already have the original commits |
Merge and rebase aren’t a simple better-or-worse choice — they produce genuinely different kinds of history, suited to different goals. Merge preserves exactly what happened, including the branching itself. Rebase produces a cleaner, linear story, at the cost of the original commits no longer technically existing.
3What Actually Happens During a Rebase
Understanding rebase’s internal mechanics removes a lot of the mystery around why it behaves the way it does.
When you rebase a branch onto a new base, Git doesn’t simply relabel or move the existing commits — it genuinely creates brand-new commits, one for each original commit being rebased, applying the same changes on top of the new base instead of the old one. Because a commit’s unique identifier is calculated from its content, including its parent, a commit built on a different base is, by definition, a completely different commit, even if the actual code change it introduces is identical.
Git identifies the commits to be moved
It determines exactly which commits, starting from where your branch diverged from the new base, need to be replayed.
Git temporarily sets those commits aside
It essentially remembers the changes each commit introduced, without keeping them attached to their original position.
Git moves the branch to the new base
The branch pointer is temporarily placed at the commit you’re rebasing onto, ready to build forward from there.
Git replays each commit’s changes, one at a time
For every original commit, in order, Git applies that same change on top of the new base, creating a brand-new commit each time.
Git handles any conflicts along the way
If a change can’t be applied cleanly because the new base has diverged too much, the rebase pauses so the conflict can be resolved before continuing.
The branch now points at the final replayed commit
Once every commit has been successfully replayed, your branch reflects an entirely new, straight-line history built on the new base.
A frequent misunderstanding is that rebase simply “moves” the existing commits, like picking up furniture and setting it down elsewhere. In reality, every rebased commit is a brand-new object with its own new identity — the originals still technically exist for a while in Git’s internal storage, but your branch no longer points to them.
4The Architecture of a Rebase: Commits, Patches, and Replays
A closer look at the pieces involved helps explain why rebase behaves the way it does under the hood.
Conceptually, Git treats each commit being rebased as a “patch” — a description of what changed, independent of exactly where it originally sat in history. Replaying a commit means applying that patch’s changes on top of whatever the new base currently looks like, then wrapping the result up as a new commit, carrying over the original author information and message, but generating a fresh identifier and a fresh parent link pointing at the newly rebased position.
Original Commit
The commit as it existed before the rebase, still technically present in Git’s storage for a period of time.
Patch
The conceptual “difference” a commit introduces, extracted so it can be reapplied elsewhere.
New Base
The commit that the sequence of patches will now be applied on top of.
Replayed Commit
The brand-new commit created after successfully applying a patch onto the new base.
Think of each commit as a recipe step written on its own index card — “add two eggs,” “stir for one minute.” Rebasing is like taking that whole stack of index cards and re-performing every step, in the same order, but starting from a different, updated set of ingredients on the counter. The written instructions stay conceptually the same, but you’re physically redoing each step fresh, against a new starting situation.
5Data Flow and Lifecycle of a Rebase Operation
Tracing a rebase from start to finish shows exactly how your branch transforms at each stage.
sequenceDiagram
participant You
participant Branch as Your Branch
participant Base as New Base
participant History as Rebased History
You->>Branch: Start rebase onto new base
Branch->>Base: Identify divergence point
Base-->>Branch: Confirm commits to replay
loop For each original commit
Branch->>History: Apply patch onto current tip
History-->>Branch: New commit created
end
Branch->>You: Branch now points to final new commit
Before the rebase begins
Your branch and the target base have diverged — meaning both contain commits the other doesn’t have. Your branch’s own commits sit on top of an older point in the target’s history, rather than its current, latest state.
During the rebase
Git works through each of your branch’s unique commits in order, temporarily pausing the process if any commit’s changes can’t be applied automatically due to a conflicting change already present in the new base.
After the rebase completes
Your branch now consists entirely of new commits, each built directly on top of the new base, forming one continuous, linear sequence with no branching point visible at all — as though your work had been written starting from that exact point in history all along.
6Interactive Rebase: Editing History Deliberately
Beyond simply moving commits to a new base, rebase also offers a mode for actively reshaping the commits themselves.
Interactive rebase presents you with an editable list of the commits about to be replayed, and lets you decide, for each one individually, exactly what should happen to it. You might choose to keep a commit exactly as it is, combine it with the commit before it, reword its message, reorder it relative to other commits, or drop it from history entirely.
Keep
Replay the commit exactly as it is, with no modification at all.
Reword
Keep the commit’s changes, but replace its message with an improved one.
Squash
Combine the commit’s changes into the commit immediately before it, merging them into a single, unified commit.
Drop
Remove the commit entirely, as though its change had never been made in the first place.
Reorder
Change the position of a commit relative to the others in the sequence being replayed.
Cleaning Up Before a Pull Request
A very common use of interactive rebase is tidying up a messy sequence of small, exploratory commits — “fix typo,” “actually fix it,” “oops” — into a small number of clear, meaningful commits before opening the work up for review.
7Advantages and Trade-offs of Rebasing
Like any powerful tool, rebase brings genuine benefits alongside real costs worth understanding clearly.
Advantages
- Produces a clean, linear, easy-to-follow history
- Removes unnecessary joining commits cluttering the log
- Lets you reshape and tidy commits before sharing them
- Makes it easier to isolate exactly which commit introduced a problem
Trade-offs
- Original commits are replaced, not preserved
- Dangerous if used on commits others have already built upon
- Can require resolving the same conflict repeatedly across many commits
- History no longer reflects exactly when and how work actually happened
A widely shared principle among experienced Git users is to never rebase commits that have already been shared with, or built upon by, other people. Doing so effectively pulls the rug out from under anyone else already relying on those original commits, forcing them to untangle a confusing mismatch between their history and yours.
8Security and Safety Considerations When Rewriting History
Because rebase fundamentally alters history rather than simply adding to it, it carries a distinct category of risk worth treating seriously.
Since rebased commits are brand-new objects with different identifiers, updating a shared branch after a rebase requires forcefully overwriting whatever was there before, rather than the normal, additive update. This is inherently a destructive operation from the perspective of anyone who already has a copy of the original commits — their local history will no longer match, and reconciling the two can range from a minor inconvenience to a genuinely confusing mess.
The specific danger of shared history
If someone else has already based their own new work on top of the original commits, and you then rebase and force-update the shared branch, their work now technically builds on commits that no longer exist in the shared history at all. This can lead to duplicated changes, confusing conflicts, and a significant cleanup effort to sort out whose history should actually be considered correct.
A Safer Default: Rebase Only Local, Unshared Work
The safest and most common practice is to only rebase commits that exist solely on your own local branch, before anyone else has seen or built upon them — commonly used to tidy up work-in-progress before opening it for review, rather than rewriting anything already shared.
9Design Patterns: When and How Teams Use Rebase Well
Used deliberately, rebase fits neatly into several well-established, healthy team workflows.
Keeping a Feature Branch Up to Date
Instead of repeatedly merging the latest changes from the main branch into a feature branch, some teams prefer rebasing the feature branch onto the latest main, keeping the eventual history clean and linear once the feature is finally merged.
Cleaning Up Before Review
Using interactive rebase to consolidate a messy sequence of exploratory commits into a small, coherent set, before ever sharing the branch, makes the eventual review far easier for teammates to follow.
Maintaining a Linear Project History
Some projects deliberately enforce a policy where every contribution must be rebased before merging, specifically to keep the entire project’s history as one clean, readable line, free of merge commits entirely.
gitGraph
commit id: "main: setup"
commit id: "main: config update"
branch feature
checkout feature
commit id: "feature: draft"
commit id: "feature: cleanup"
checkout main
commit id: "main: hotfix"
checkout feature
commit id: "feature: rebased draft"
commit id: "feature: rebased cleanup"
checkout main
merge feature id: "clean, linear merge"
10Anti-Patterns: Common Rebase Mistakes and Misuse
Just as clearly as there are healthy patterns, there are well-known ways rebase gets misused, often with painful consequences.
Problem
Rebasing a branch that other teammates have already pulled and built additional work on top of, then force-updating the shared copy.
Why It’s Harmful
Everyone else’s local history now conflicts with the rewritten shared history, often resulting in duplicated commits, confusing merge attempts, and significant time spent untangling what should be considered the “true” history.
Correct Approach
Reserve rebase for commits that are still entirely local and unshared, and rely on merging once work has already been shared with others.
Problem
Using interactive rebase to squash together commits that actually represent distinct, meaningful changes, purely for the sake of having “fewer commits.”
Why It’s Harmful
It destroys useful granularity in history, making it harder later to isolate exactly which specific change introduced a bug, since multiple unrelated changes are now bundled into one commit.
Correct Approach
Squash only commits that genuinely represent the same logical change, such as a fix to a typo made earlier in the same piece of work, and preserve distinct, meaningful commits as separate entries.
Problem
Rebasing a very long-lived branch against a rapidly changing base, over and over, resolving the same or similar conflicts repeatedly each time.
Why It’s Harmful
Frequent, painful, repeated conflict resolution signals a deeper structural problem, often that the branch has been kept separate for far too long and has drifted too far from the base it needs to eventually rejoin.
Correct Approach
Favor smaller, shorter-lived branches that are integrated back frequently, reducing how far any single branch can drift before rebasing becomes painful.
11Best Practices for Rebasing Safely
A handful of consistent habits keep rebase a genuinely useful tool rather than a source of team-wide headaches.
Only rebase commits that are still local and unshared
Treat anything already pushed and visible to teammates as effectively permanent, and rely on merging from that point onward.
Communicate clearly if a shared rebase is genuinely necessary
In rare cases where rewriting shared history is unavoidable, make sure every affected teammate knows exactly what happened and how to reconcile their own local copy.
Rebase in small, frequent steps rather than one giant one
Keeping branches short-lived and rebasing often avoids the pain of resolving a huge backlog of conflicts all at once.
Use interactive rebase thoughtfully, not automatically
Only squash or reorder commits when doing so genuinely improves clarity, rather than treating “fewer commits” as an automatic goal on its own.
Double-check the result before forcefully updating anything shared
Review the rebased history carefully to confirm it looks correct before overwriting any branch other people might rely on.
12Rebase in the Real World
Rebase plays a distinct, deliberate role in the workflows of many serious, high-quality software projects.
Large, disciplined open-source projects — including some of the most heavily contributed-to codebases in the world — commonly require contributors to rebase their work before it’s accepted, specifically to maintain a clean, linear project history that’s easier to review and reason about over the project’s entire lifetime. Individual engineers frequently use local, unshared rebasing purely as a personal tidying step, cleaning up their own exploratory commits into a clear, professional-looking sequence before ever opening a pull request.
Linear-History Projects
Some projects deliberately maintain a strict, merge-commit-free history, relying entirely on rebase to integrate every single contribution as a clean, linear sequence.
Personal Commit Hygiene
Individual developers commonly rebase their own local, unshared branches purely to present a clean, professional set of commits before requesting review from teammates.
13Frequently Asked Questions
Not immediately. The original commits typically remain in Git’s internal storage for a period of time, even though your branch no longer points to them, meaning recovery is often still possible shortly after a rebase if something goes wrong.
Because a commit’s identifier is calculated from its full contents, including its parent commit, replaying a commit onto a different parent inevitably produces a different identifier, even if the actual code change is identical.
It produces a cleaner-looking, linear history, but “cleaner” doesn’t automatically mean “better” — merging preserves exactly what happened, including genuine branching, which some teams value more than a simplified, linear story.
The rebase pauses at that specific commit, letting you resolve the conflict directly, after which the process continues replaying the remaining commits in sequence.
Yes. Rebase operations can typically be aborted partway through, restoring your branch to exactly the state it was in before the rebase began.
It’s generally discouraged, since it forces everyone else to reconcile their local history with the rewritten version. When genuinely necessary, it should be done deliberately, with clear communication to everyone affected beforehand.
Yes. Beyond rewording messages, interactive rebase allows editing a commit’s actual changes, combining it with others, reordering it, or removing it entirely from the sequence.
14Summary and Key Takeaways
Rebase gives you the power to reshape history itself — taking a sequence of commits and rebuilding them, one at a time, on top of a new starting point, producing a clean, linear story instead of a tangle of joined branches. That power comes with real responsibility: because rebased commits are entirely new objects, using rebase on anything already shared with others can create genuine confusion and conflict across a team. Used thoughtfully, on local, unshared work, rebase is one of the most valuable tools for producing a readable, professional project history. Used carelessly, on commits others already depend on, it becomes a source of exactly the kind of chaos it was meant to prevent.
Key Takeaways
- Rebase replays commits onto a new base — creating brand-new commits rather than moving the original ones.
- It produces a clean, linear history — unlike merging, which preserves branching with a dedicated joining commit.
- Rebased commits get new identifiers — since a commit’s identity depends on its parent, which has now changed.
- Interactive rebase allows deliberate editing — rewording, combining, reordering, or dropping commits before sharing them.
- Never rebase already-shared commits carelessly — doing so can create serious confusion for anyone who already built on them.
- Short-lived branches make rebasing painless — the longer a branch drifts, the more conflict resolution a rebase can require.
- Rebase and merge solve the same goal differently — choosing between them depends on whether your team values a clean linear history or a fully preserved, branching one.