Git Rebasing
Replay commits for a linear history. Basic and interactive rebase, squashing, recovering with --abort and reflog, force-with-lease, and when to merge instead.
What Rebase Does
Rebase moves or reapplies commits onto a new base commit. Instead of combining two branches with a merge commit, rebase rewrites the branch so it appears to start from a later point in history.
Suppose feature/login was created from main, but main has advanced since then. Rebasing feature/login onto main replays the feature branch commits on top of the newer main.
git switch feature/login
git rebase main
This can make history look cleaner and more linear.
Tip: Rebase rewrites commit history. That is powerful, but it means you should be careful with commits other people are already using.
Why Developers Rebase
Common reasons to use rebase include:
- updating a feature branch with the latest
main - cleaning up commit history before a pull request
- squashing many small commits into one logical commit
- reordering or editing recent commits
Rebase does not add new project content by itself. It changes how commits are arranged in history.
Basic Rebase Example
Imagine this history:
main: A → B → Cfeature/login: A → D → E
After rebasing feature/login onto main, the feature becomes:
main: A → B → Cfeature/login: A → B → C → D' → E'
The apostrophes matter. Git usually creates new commit IDs because it is replaying the changes, not simply moving old commits.
Use:
git switch feature/login
git rebase main
If everything applies cleanly, you get the same code changes on a newer base.
Rebase Conflicts
Rebase can produce conflicts just like merge. When that happens, Git pauses and asks you to resolve them.
A typical flow looks like this:
git rebase main
git status
Edit the conflicted file, remove conflict markers, then continue:
git add app.js
git rebase --continue
If another conflict appears, repeat the process.
If you want to stop and return to the original state:
git rebase --abort
Interactive Rebase with -i
Interactive rebase lets you edit a series of recent commits. It is one of the most useful cleanup tools in Git.
To review the last three commits:
git rebase -i HEAD~3
Git opens your editor with something like:
pick a1b2c3d Add login form
pick d4e5f6g Fix typo in login form
pick h7i8j9k Update login button color
You can change the action for each commit.
Common Interactive Actions
pick: keep the commit as isreword: keep the content but edit the messageedit: pause so you can modify the commitsquash: combine this commit with the previous onedrop: remove the commit
Squashing Commits
Suppose your history contains several tiny “fix typo” commits that belong with the main feature. Interactive rebase can combine them.
Example:
pick a1b2c3d Add login form
squash d4e5f6g Fix typo in login form
squash h7i8j9k Update login button color
After saving, Git combines the commits and lets you write a final commit message.
This is useful before opening a pull request because reviewers often prefer a clean, logical commit history.
Note: Squashing is best used on your own unpublished work. Avoid rewriting shared history unless your team explicitly agrees on that workflow.
Rebase vs Merge
Both rebase and merge combine lines of development, but they tell the story differently.
| Topic | Merge | Rebase |
|---|---|---|
| History shape | Preserves branch structure | Creates a more linear history |
| New commit IDs | Usually no rewrite of existing commits | Rewrites rebased commits |
| Safety for shared history | Safer | Needs more care |
| Typical use | Integrating branches | Cleaning history or updating a feature branch |
Simple Rule of Thumb
- Use merge when you want to preserve real branch history
- Use rebase when you want a cleaner branch history before integration
A Safe Team Habit
A common safe habit is:
- Rebase your own feature branch onto the latest
main - Push the updated branch
- Merge it through a pull request
Example:
git switch main
git pull
git switch feature/login
git rebase main
git push --force-with-lease
--force-with-lease is safer than plain --force because it checks whether the remote changed in unexpected ways.
What You Should Remember
Rebase replays commits onto a new base, which can make history look cleaner. Interactive rebase is especially useful for rewording, reordering, or squashing recent commits. But because rebase rewrites history, it should be used carefully on shared branches.
If you are unsure, use merge. If you want a polished branch before review, rebase can be a great tool.
Test Your Understanding
Exercise 1: Understanding rebased commits
Scenario: Your feature branch had commits D and E, and you rebased it onto a newer main with commits A, B, and C.
Question: Why did the tutorial show the rebased branch as A -> B -> C -> D' -> E' instead of keeping the old D and E exactly?
Exercise 1: Understanding rebased commits
Identify why rebased commits usually receive new identities.
Exercise 2: Cleaning up tiny commits
Scenario: Your feature branch contains Add login form, Fix typo in login form, and Update login button color as three small related commits.
Question: Which interactive rebase action from the tutorial is designed to combine the later two into the first logical commit?
Exercise 2: Cleaning up tiny commits
Choose the interactive rebase action that combines related commits.
Exercise 3: Pushing a rebased branch safely
Question: After rebasing your own feature branch onto the latest main, why did the tutorial recommend git push --force-with-lease instead of plain --force?
Exercise 3: Pushing a rebased branch safely
Pick the reason force-with-lease is safer after a history rewrite.
Frequently Asked Questions
What is the difference between git rebase and git merge?
Both integrate one branch into another; they differ in what history they leave behind. git merge creates a merge commit joining the two lines of development, preserving exactly what happened including the fact that work happened in parallel. git rebase replays your commits on top of the target branch, producing a straight line as though you had started from the latest code. Merge is honest about history and clutters the graph; rebase is tidy and rewrites commit hashes. Most teams rebase local feature branches before opening a pull request, then merge the result.
Is it safe to rebase a branch that has been pushed?
Only if you are the only person working on it. Rebasing creates new commits with new hashes, so the remote branch and your local one diverge and git push is rejected until you force it. Use git push --force-with-lease rather than --force: it refuses the push if someone else has added commits you have not seen, which turns silent data loss into an error message. Never rebase a shared branch such as main. Everyone who has pulled it will be stranded on commits that no longer exist.
How do I squash multiple commits into one?
Run git rebase -i HEAD~n where n is how many commits back you want to go, then change pick to squash (or s) on every commit except the first. The first stays pick because the others are folded into it. Git then opens an editor to combine the messages. If you only want the first commit's message, use fixup instead of squash to discard the others. Many platforms also offer "Squash and merge" on a pull request, which achieves the same result without rebasing locally.
How do I get out of a rebase that has gone wrong?
git rebase --abort returns the branch to exactly where it was before the rebase started, at any point during the process. That is the escape hatch to reach for whenever you are unsure. If the rebase has already completed and you want the old state back, git reflog lists where HEAD has been, and git reset --hard HEAD@{n} moves you to the entry before the rebase. The reflog is why a completed rebase is recoverable and why "I lost my work" is almost never actually true.
Why do I keep hitting the same conflict repeatedly during a rebase?
Because a rebase replays each commit in turn, so a line you changed in several commits conflicts once per commit. Resolve the file, git add it, then git rebase --continue to move to the next one. Two things help: squashing related commits before rebasing so there are fewer replays, and enabling git config --global rerere.enabled true, which records how you resolved a conflict and reapplies that resolution automatically the next time the identical conflict appears.
What does git pull --rebase do and should I use it?
It fetches the remote branch and replays your local commits on top of it, instead of creating a merge commit. Without it, a plain git pull on a branch that has moved on both sides produces a "Merge branch 'main' of ..." commit, and a busy repository fills with them. Setting git config --global pull.rebase true makes it the default and keeps history linear. It is safe for your own unpushed commits, which is exactly what git pull is dealing with.