Table of Contents
3 Common & Frustrating Scenarios
When using Git, your commit history can sometimes become unintentionally complex. Here are some classic scenarios that many developers have faced.
Scenario 1: Accidentally Branched from Another Feature Branch
You were supposed to create your new work branch (my-feature) from the latest main, but you accidentally branched off a colleague’s work-in-progress branch (dev-feature). Now your Pull Request includes commits that have nothing to do with your work.
|
1 2 3 4 5 6 7 8 9 |
# [Problematic State] * commit C (HEAD -> my-feature) * commit B * commit A (dev-feature) <-- Shouldn't have branched from here | * Latest main (main) |/ * ... |
Scenario 2: Mixed Multiple Features in a Single Branch
While working on a branch (work-branch), you realize that commits C and D are actually for a completely different feature (new-idea). You want to cleanly separate just these two commits into a new branch.
|
1 2 3 4 5 6 7 8 9 10 |
# [Problematic State] * commit E (HEAD -> work-branch) * commit D ┓ * commit C ┛ <-- Want to separate only these two * commit B * commit A * Latest main (main) * ... |
Scenario 3: Started New Work on an Outdated Branch
You started some new work (new-work) on a branch you created months ago (old-feature). Meanwhile, the main branch has evolved significantly, and your new commits are now sitting on top of a very old history.
|
1 2 3 4 5 6 7 8 9 10 11 |
# [Problematic State] * commit Y (HEAD -> new-work) * commit X | * A much newer main (main) | | * ... | |/ | * commit B (old-feature) <-- Outdated base |/ * An old commit from main |
All of these problems can be solved with git rebase --onto.
The Key to the Solution: What is git rebase --onto?
git rebase --onto is a powerful rebase command that lets you specify exactly which commits to move (from..to) and where to move them (onto).
Basic Command Syntax
|
1 |
git rebase --onto <new-base> <old-base> <branch-to-move> |
Understanding these three arguments is the key to mastering this command.
Practical Guide: Commands for Each Scenario
Scenario 1: Branched from Another Feature Branch
Let’s look at a concrete example for the most common case: “Scenario 1: Accidentally branched from another feature branch.”
Goal: Re-base the my-feature branch so it originates from main instead of dev-feature.
|
1 2 3 4 5 6 7 |
# Explanation: # --onto main <-- The new base is "main" # dev-feature <-- The old base is "dev-feature". Commits after this point will be moved. # my-feature <-- The branch whose commits we want to move is "my-feature". git rebase --onto main dev-feature my-feature |
With this single command, Git reconstructs the history as follows:
|
1 2 3 4 5 6 7 8 9 10 |
# [Ideal State After Rebase] * commit C' (HEAD -> my-feature) * commit B' | * Latest main (main) | | * commit A (dev-feature) | |/ |/ * ... |
The commits from my-feature (B’ and C’) have been cleanly moved on top of main, and are now independent of dev-feature.
Scenario 2: Separating Commits from a Branch
Goal: Move commits C and D from work-branch to a new new-idea branch, based on main. In this case, we use commit IDs directly.
|
1 2 3 4 5 6 7 8 9 10 |
# Get the parent commit ID of the range you want to move. git log --oneline work-branch # Copy the ID of commit B (the parent of C) # 1. Create a new branch at the current position git switch -c new-idea work-branch # 2. Use rebase --onto to move the commits # --onto [new-base] [parent-of-the-range] [end-of-the-range] git rebase --onto main <commit_B_ID> <commit_D_ID> |
This will place only commits C and D (as new commits) onto the main branch in your new new-idea branch.
Scenario 3: Moving from an Old Base to the Latest Base
Goal: Move the new-work branch from its outdated base (old-feature) to the tip of the latest main.
|
1 2 3 |
# --onto [new-base] [old-base] [branch-to-move] git rebase --onto main old-feature new-work |
The command structure is identical to Scenario 1. rebase --onto works perfectly, no matter how far apart the branches are.
Important Safety Tips & Tricks
Pushing to a Remote Repository
Rebasing rewrites your local commit history. If you rebase a branch that has already been pushed to a remote, you must avoid git push --force, which can cause problems for your teammates. Instead, use the safer --force-with-lease.
|
1 2 3 |
# Force push while ensuring you don't overwrite someone else's work git push --force-with-lease origin <branch-name> |
Handling Conflicts During a Rebase
Since a rebase applies commits one by one, you may encounter a merge conflict. Don’t panic; follow these steps to resolve it.
- Resolve the conflicting files: Open the files in your editor and fix the code, removing the
<<<<<<<,=======, and>>>>>>>markers. - Stage the resolved files:
1git add <path/to/resolved/file> - Continue the rebase:
1git rebase --continue
If you get overwhelmed and want to stop the rebase, you can always return to the state before you started with this command:
|
1 |
git rebase --abort |
Conclusion
git rebase --onto might seem complex at first, but once you understand the concept of “move this range of commits onto that new base,” it becomes a powerful tool for tidying up your Git history.
If any of the scenarios described here feel familiar, give this command a try. Just remember to communicate with your team when rewriting the history of a shared branch.
