Git Rebase: The Complete Guide to Rebasing, Undoing, and Advanced Workflows
Rebasing is one of Git's most powerful and frequently misunderstood tools. While merging integrates changes by creating a new merge commit, rebasing rewrites history by moving your commits onto a new base. This creates a clean, linear commit history.
However, rewriting history comes with risks. This guide covers how to rebase onto main, resolve conflicts, undo mistakes, utilize advanced interactive workflows, and when to avoid rebasing entirely.
How to Rebase a Feature Branch onto Mainโ
Rebasing is highly effective when updating your local feature branch with the latest commits from the remote main branch before opening a pull request.
The Rebase Workflowโ
- Switch to your feature branch:
git switch my-feature-branch - Fetch the latest remote changes:
This ensures your local copy of
git fetch originorigin/mainis completely up to date. - Rebase your branch:
Git will temporarily lift your local commits, apply the new remote commits from
git rebase origin/mainmain, and then replay your commits on top.
Resolving Rebase Conflictsโ
If your branch edits the same lines as the remote branch, Git will pause the rebase.
- Open the conflicted files and manually resolve the conflict markers (
<<<<<<<,=======,>>>>>>>). - Stage the resolved files:
git add path/to/conflicted_file - Continue the rebase:
(Repeat this process for any subsequent commits. Do not run
git rebase --continuegit commitduring a rebase).
Aborting a Rebaseโ
If the conflicts are too complex or you make a mistake, you can safely cancel the entire operation and restore your branch to its original state:
git rebase --abort
Pushing After a Rebaseโ
Because rebasing rewrites history, Git will reject a standard git push. You must force the push. Always use --force-with-lease instead of a plain --force:
git push --force-with-lease
The --force-with-lease flag is a safety check: it refuses to push if someone else has pushed new commits to the remote feature branch since you last pulled.
When Rebasing is Not Recommended (The Golden Rule)โ
There are two primary situations where you should avoid rebasing:
1. Public or Shared Branchesโ
You should never rebase a branch that has been pushed to a remote repository and pulled by other developers (3).
- Why: Rebasing replaces your original commits with brand new ones. If other developers are already working on top of your original commits, their local history will no longer match the remote history. When they try to sync, they will be forced to merge divergent histories, leading to severe merge conflicts and duplicated commits.
- Alternative: Use
git mergefor shared branches. It preserves original commits and records the exact merge point explicitly.
2. When Preserving Raw Historical Context is Criticalโ
A linear git log is clean, but it hides the exact chronological order of how features converged. If you need to trace the exact, unedited timeline of when development paths crossed or keep track of every individual micro-commit, stick to git merge.
How to Undo a Completed Git Rebaseโ
If you successfully completed a rebase but realize it introduced bugs or was done on the wrong branch, you can revert it.
Method A: Undoing Local-Only Rebasesโ
If you haven't pushed the rebased branch yet, you can reset your branch pointer using the Git reflog (5).
- View your HEAD history using
git reflog:The output will look similar to this:git refloga1b2c3d HEAD@{0}: rebase (finish): returning to refs/heads/my-feature-branch
e4f5g6h HEAD@{1}: rebase (start): checkout origin/main
i7j8k9l HEAD@{2}: commit: Add validations to login form - Locate the commit hash before the rebase started. In the log above,
HEAD@{2}(commiti7j8k9l) was the last commit before the rebase began. - Reset your branch to that commit:
(Alternatively, you can run
git reset --hard HEAD@{2}git reset --hard ORIG_HEADimmediately after the rebase completes, as Git stores the pre-rebase HEAD in theORIG_HEADvariable. However, this is overwritten by subsequent dangerous commands).
Method B: Reverting a Pushed Rebaseโ
If you already force-pushed the rebased branch to a shared repository, running git reset --hard and force-pushing again will disrupt your team. The safest approach is to use git revert to apply inverse changes (6).
- Find the commits introduced during the rebase by comparing your branch to
main:git log main..my-feature-branch - Create new commits that revert the changes:
git revert <commit-hash-1> <commit-hash-2> - Push the new revert commits to the remote:
git push
Advanced & Unusual Git Rebase Workflowsโ
Beyond basic updating, git rebase can be used to clean up local commits or restructure branches.
1. Interactive Rebase (git rebase -i)โ
Interactive mode allows you to edit, combine, or delete commits in your branch history. This is ideal for cleaning up "work-in-progress" commits before opening a pull request.
To edit the last 5 commits:
git rebase -i HEAD~5
This opens your text editor with a list of commits prefixed by commands:
pick 623f71c Add user login form
pick 1b7c84a Fix typo in user model
pick d9e8f2e Add validations to login form
pick 5a4b3d1 Forgot to add a file, adding it now
You can change pick to other commands:
- reword (r): Keeps the commit contents but lets you rewrite the commit message.
- squash (s): Combines the commit with the previous one and prompts you to merge the commit messages.
- fixup (f): Combines the commit with the previous one but discards its commit message (perfect for quick typo fixes).
- drop (d): Removes the commit entirely.
2. Splitting a Commitโ
If you made a large commit that should be split into smaller, independent commits:
- Run interactive rebase and mark the target commit as
edit:edit bf123ac Add user profile page and refactor API calls - Save and close the editor. Git will pause at the target commit.
- Reset the commit pointer without losing your files:
git reset HEAD^ - Stage and commit the first group of files:
git add src/components/Profile.js
git commit -m "Add user profile page" - Stage and commit the remaining changes:
git add src/utils/api.js
git commit -m "Refactor API calls" - Complete the rebase:
git rebase --continue
3. Restructuring Branches with git rebase --ontoโ
The --onto flag allows you to transplant a series of commits from their current base to a new one.
Suppose you have a feature branch my-feature branched off main at commit C, but you want to move it to a different branch new-base.
Before:
A -- B -- C -- D (main)
\
E -- F -- G (my-feature)
To move commits E, F, and G onto new-base:
git rebase --onto new-base C my-feature
After:
A -- B -- C -- D (main)
\
H -- I (new-base)
\
E -- F -- G (my-feature)
Sourcesโ
- [1] Git Reference Documentation: git-rebase
- [2] Git Reference Documentation: git-push
- [3] Git Book: The Golden Rule of Rebasing
- [4] Atlassian Git Tutorials: Git Merge vs. Rebase
- [5] Warp Dev: How To Undo a Git Rebase
- [6] Graphite Guides: Git undo rebase
- [7] GeeksforGeeks: How to Undo a Git Rebase
