Rebasing
Rebasing is the other way to bring branches together. Instead of tying two histories with a merge commit, git rebase replays your branch's commits on top of another branch, as if you had started your work from its latest commit. The result is a straight, tidy history.
The situation
Sam has two commits on a soups branch. Meanwhile main has moved on:
git log --oneline --graph --all
* 34bcd14 (main) Add chives to omelette | * c3f01f0 (HEAD -> soups) Add pea soup | * dc6ea2b Add tomato soup |/ * 90c0089 Add omelette recipe * c4f5e47 Add pancake recipe
A merge would join them with a merge commit. A rebase instead moves the soup commits so they start from the tip of main.
Rebasing a branch
While on the branch you want to move, name the branch to rebase onto:
git rebase main git log --oneline --graph --all
Successfully rebased and updated refs/heads/soups. * 5da11b5 (HEAD -> soups) Add pea soup * ff51e53 Add tomato soup * 34bcd14 (main) Add chives to omelette * 90c0089 Add omelette recipe * c4f5e47 Add pancake recipe
The history is now a straight line. The soup commits come after "Add chives to omelette", and their hashes have changed — rebasing creates new commits with the same changes and messages but a different parent:
before: after:
C ◀── D soups C' ◀── D' soups
/ /
A ◀── B ◀── E main A ◀── B ◀── E main
Now main can take the soups with a simple fast-forward, and there's no merge commit:
git switch main git merge soups
Switched to branch 'main' Updating 34bcd14..5da11b5 Fast-forward pea-soup.txt | 1 + tomato-soup.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 pea-soup.txt create mode 100644 tomato-soup.txt
Merge or rebase?
| Merge | Rebase | |
|---|---|---|
| History | Shows exactly what happened, including parallel work | A straight line, as if work happened in sequence |
| Existing commits | Unchanged | Replaced by new copies |
| Conflicts | Resolved once, in the merge commit | May need resolving once per replayed commit |
| Safe on shared branches | Yes | No — only on commits you haven't shared |
Both are fine choices, and teams pick a style. A common combination: rebase your own feature branch onto the latest main to keep it up to date and tidy, then merge it into main through a pull request.
The golden rule
main or another shared branch.If you rebase a feature branch you've already pushed (and only you work on), the next push must be forced with git push --force-with-lease (lesson 32).
Conflicts during a rebase
Each commit is replayed one at a time, so a conflict stops the rebase at that commit:
git rebase main
Auto-merging tomato-soup.txt CONFLICT (content): Merge conflict in tomato-soup.txt error: could not apply 9a7a2d7... Make tomato soup spicy hint: Resolve all conflicts manually, mark them as resolved with hint: "git add/rm <conflicted_files>", then run "git rebase --continue". hint: You can instead skip this commit: run "git rebase --skip". hint: To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply 9a7a2d7... Make tomato soup spicy
Resolve it as with a merge conflict: edit the file, then git add it. Instead of committing, tell the rebase to carry on:
echo "Tomato soup: tomatoes, onion, stock, basil, chilli" > tomato-soup.txt
git add tomato-soup.txt
GIT_EDITOR=true git rebase --continue
[detached HEAD 6aca843] Make tomato soup spicy 1 file changed, 1 insertion(+), 1 deletion(-) Successfully rebased and updated refs/heads/spicy.
(git rebase --continue opens your editor so you can adjust the commit message; the GIT_EDITOR=true prefix here just accepts it unchanged.) While a rebase is paused you have three choices:
| Command | Effect |
|---|---|
git rebase --continue | Carry on after resolving and staging |
git rebase --skip | Drop the commit that conflicted and continue |
git rebase --abort | Cancel the whole rebase and go back to how things were |
Pulling with rebase
The most everyday use of rebasing is updating a branch from the server. git pull --rebase fetches new commits and replays your local, unpushed commits on top of them, instead of creating a merge commit each time you pull (lesson 34).
Rebasing onto another base
--onto moves a range of commits to a completely different starting point — for example, if you accidentally started a branch from another feature branch instead of main:
# move the commits that are on my-feature but not on other-feature onto main
git rebase --onto main other-feature my-feature
Try it yourself
Create a branch with two commits, then add a commit to main. Rebase the branch onto main and check with git log --oneline --graph --all that the history is a straight line. Fast-forward main to include it.