Running git rebase main replays the commits on your feature branch on top of the current tip of main, so your branch looks as though you started from the latest code, and it is safe on a branch only you use as long as you never rewrite history others have pulled.
Rebase gives a clean linear history and has a reputation for destroying work. Both are true, and the difference is a handful of rules.
Table of contents
- What rebasing onto main does
- The safe workflow, step by step
- Resolving conflicts mid-rebase
- Rebase or merge: how to choose
- Mistakes people make with rebase
- Where rebasing meets deployment
- Tidy your commits first with interactive rebase
- How this fits the rest of the stack
- FAQ
What rebasing onto main does
Your feature branch started from a commit on main. Since then, main has moved on. Teammates merged pull requests, and your branch is now behind. You have two ways to catch up: merge main into your branch, or rebase your branch onto main.
Rebase works by unwinding your commits. Git finds where your branch diverged from main, sets your commits aside, moves the branch pointer to the new tip of main, and then replays your commits one at a time on top. Each replayed commit is a new commit with a new hash, because its parent changed. The content of your changes is the same. The history is not.
The result is a straight line: main’s commits, then yours. No merge commit, no diamond in the graph. When the branch is later merged into main it can fast-forward, and the log reads like a story instead of a braid.
The Git book chapter on rebasing shows the diagrams, and they are worth a look if the commit-replay idea is new. The thing to carry forward is that rebase rewrites history, and everything awkward about it follows from that.
The safe workflow, step by step
Do it in this order and most problems never appear.
git switch my-feature
git fetch origin
git rebase origin/main
Fetching first matters. Rebasing onto your local main rebases onto a stale copy if you have not pulled. Rebasing onto origin/main after a fetch guarantees you are using what the remote actually has.
Before you start, make sure your working tree is clean. Commit or stash anything pending. A dirty tree makes a rebase refuse to start or, worse, tangles unrelated changes into a conflict.
If the rebase finishes with no conflicts, run your tests. A clean rebase is not proof the code still works, because the replayed commits now sit on different code than they were written against. Then push. Since the branch’s commits have new hashes, a normal push is rejected, and you need a force push. Use the safe form:
git push --force-with-lease origin my-feature
The lease refuses to overwrite the remote if it contains commits you have not seen. A plain force push does not. For background on forced pushes see git push -f, and if you need to recover from the opposite problem, git pull force overwrite covers it.
Resolving conflicts mid-rebase
Conflicts during a rebase happen one commit at a time. Git stops at the first commit that does not apply cleanly, marks the conflict in the files and waits.
The loop is always the same:
- Run
git statusto see the conflicted files. - Open each file, resolve the markers, and keep the right combination of both sides.
- Stage the resolved files with
git add. - Continue with
git rebase --continue.
Do not run git commit in the middle of a rebase unless you mean to. Git creates the commit for you on continue.
If it goes wrong, you can always back out. git rebase --abort returns the branch to exactly where it was before you started. That single command is the reason rebasing is less dangerous than it feels. Until you push, nothing is lost.
A tip when there are many conflicts: they often come from the same underlying change repeated across several of your commits. Squash your branch into a few meaningful commits first with an interactive rebase, then rebase onto main. Resolving a conflict once is better than resolving it eight times.
If you resolve a conflict wrongly and only notice later, the reflog saves you. git reflog lists where the branch pointed before each operation, and you can reset to the entry from before the rebase.
Rebase or merge: how to choose
Neither is right everywhere. The decision is mostly about who else has the branch and what history you want.
| Situation | Better choice |
|---|---|
| Private feature branch, updating from main | Rebase |
| Branch shared with other people | Merge |
| Preserving the exact record of what happened | Merge |
| Wanting a linear, bisectable log | Rebase, then fast-forward merge |
| Long-lived branch with many merges | Merge |
The golden rule is simple: do not rebase commits that exist outside your repository, meaning anything other people may have pulled. If you rewrite a shared branch, their copy and yours diverge, and the cleanup is painful for everyone.
Many teams settle on a convention: rebase locally while a branch is private, then merge the finished work into main with a merge commit or a squash. The existing guide to merging a branch into main covers the other half of that workflow. Pick one convention, write it down in the repository, and stop arguing about it.
Mistakes people make with rebase
A few errors repeat. Knowing them is faster than learning them by losing a day.
Rebasing the wrong direction. Running git rebase my-feature while on main moves main onto your branch, which is almost never what you meant. The rule: check out the branch you want to move, then rebase onto the target.
Force pushing blindly. A plain --force can erase a teammate’s commits. Prefer --force-with-lease.
Rebasing a branch with merge commits. By default a rebase flattens merges, which can silently drop conflict resolutions. Use --rebase-merges when the shape matters.
Skipping the tests afterward. Rebases compile fine and fail at runtime because code you depended on changed underneath you.
Treating it as a way to hide a mess. A tidy history is good, but do not rewrite commits that others have already reviewed, or reviewers lose their place.
A last habit: before a risky rebase, create a safety branch with git branch backup-my-feature. It costs nothing and gives you a known-good point to return to.
Where rebasing meets deployment
If your deploys trigger from pushes to a branch, a force push triggers a new build with new commit hashes. That is normal, but it means a branch preview or staging environment rebuilds from the rewritten history. Make sure automatic deploys are tied to branch names, not specific commit hashes you have noted elsewhere.
Platforms that deploy from GitHub watch a branch and rebuild when it changes. On RunxBuild, services and static sites deploy from a repository branch, with build logs for each deploy and rollback to a previous one if a deploy goes bad. A rebased branch simply produces a new deploy. The GitHub getting started guide describes how the connection works.
Tidy your commits first with interactive rebase
Rebasing onto main updates your branch. Rebasing interactively against your own base tidies it. Both are the same machinery with a different goal.
git rebase -i origin/main
Git opens an editor listing your commits, oldest first, each with a verb. Change pick to squash to fold a commit into the one above it, reword to fix a message, fixup to fold without keeping the message, and drop to remove a commit entirely. You can also reorder lines to reorder commits.
The classic use is cleaning up a branch that accumulated commits like wip, fix typo and try again. Squash those into a handful of meaningful commits before review. Reviewers read your history as an explanation, so make it one.
Two cautions. Interactive rebase of commits that are already on a shared branch causes the same problems as any rewrite. And if you reorder commits that touch the same lines, expect conflicts. Do the tidy-up before you open the pull request, then update from main with a plain rebase as needed. If you get lost, git rebase --abort still returns you to the start.
How this fits the rest of the stack
Cleaner history is the easy half of shipping; the other half is what the build costs to run once it merges. Before you point a freshly rebased branch at production, check the monthly line items in the RunxBuild hosting calculator. When the branch is ready to deploy from GitHub, the RunxBuild dashboard is where you connect it.
Useful related references:
- Removing a Git Commit: reset, rebase, and Which One Is Safe
- git push origin main: What Each Word Does and Why It Fails
- Git Merge Branch Into Main Without the Conflict Panic
- Services on RunxBuild
FAQ
What does git rebase main do?
It takes the commits on your current branch and replays them on top of the latest commit on main. The branch then looks like it started from today’s main, and the history stays a straight line without a merge commit.
Should I rebase onto main or origin/main?
Fetch first, then rebase onto origin/main. Rebasing onto a local main that has not been updated rebases onto stale code.
How do I undo a rebase?
If it is still in progress, run git rebase —abort. If it already finished, find the previous state with git reflog and reset the branch to the entry from before the rebase.
Is git push —force-with-lease safer than —force?
Yes. It refuses to overwrite the remote branch if it contains commits you have not fetched, so you cannot accidentally erase a teammate’s work. Use it after every rebase of a pushed branch.
When should I merge instead of rebase?
Merge when the branch is shared with others or when you want an exact record of what happened. Rebase private branches to keep a clean linear history.