Fix: Git Rebase Conflict – How to Resolve Conflicts and Continue or Abort
Quick Answer
How to resolve conflicts during git rebase, use rebase --continue, --abort, or --skip, and avoid common rebase pitfalls.
The Error
You run git rebase and Git stops with:
Auto-merging src/utils.js
CONFLICT (content): Merge conflict in src/utils.js
error: could not apply 3a7b2c1... Add input validation
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 3a7b2c1... Add input validationIf you run git status during a paused rebase, you see:
interactive rebase in progress; onto f8e9d2a
Last command done (1 command done):
pick 3a7b2c1 Add input validation
Next commands to do (2 remaining commands):
pick 5c1d8f3 Refactor error handler
pick 9e4a7b6 Update tests
(use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'feature' on 'f8e9d2a'.
(fix conflicts and run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: src/utils.jsWhy This Happens
To understand rebase conflicts, you first need to understand what rebase does and how it differs from merge.
Rebase vs Merge
Both git merge and git rebase integrate changes from one branch into another, but they do it differently.
Merge takes the tips of two branches and creates a new merge commit that joins them together. The existing commits on both branches remain unchanged. This preserves the exact history of how development happened, but it creates a non-linear history with merge commits.
Rebase takes your commits, detaches them from their original base, and replays them one by one on top of a new base commit. Each of your original commits is recreated as a new commit with a new hash. The result is a linear history that looks as if you wrote all your changes after everything on the target branch.
Here is the key difference that causes conflicts: during a merge, Git resolves all changes in a single step. During a rebase, Git replays your commits one at a time. Each replayed commit can potentially conflict with the new base. This means you may have to resolve conflicts multiple times during a single rebase — once for each commit that touches the same lines as the upstream changes.
Why conflicts happen during rebase specifically
Consider this scenario:
- You branch off
mainat commit A. - You make three commits on your
featurebranch: B, C, and D. - Meanwhile,
mainmoves forward with commits E and F that modify some of the same files you changed. - You run
git rebase mainfrom yourfeaturebranch.
Git tries to replay commit B on top of F. If B and E or F changed the same lines, you get a conflict. After you resolve it and continue, Git tries to replay commit C. If C also touches those lines, you get another conflict. And so on for commit D.
This is different from a merge conflict, where you resolve everything once. With rebase, the per-commit replay can surface the same logical conflict repeatedly across multiple commits.
Fix 1: Resolve the Conflict and Continue the Rebase
This is the standard approach. When Git stops on a conflict during rebase, follow these steps:
Step 1: Check which files have conflicts:
git statusLook for files under “Unmerged paths” marked as both modified.
Step 2: Open each conflicting file and find the conflict markers:
<<<<<<< HEAD
function validate(input) {
return input.length > 0;
}
=======
function validate(input) {
if (!input) throw new Error('Input required');
return input.trim().length > 0;
}
>>>>>>> 3a7b2c1 (Add input validation)During a rebase, the meaning of the two sides is different from a merge:
<<<<<<< HEADis the version from the branch you are rebasing onto (plus any of your commits that have already been replayed successfully). This is the “current” state of the code at this point in the rebase.>>>>>>> 3a7b2c1is the version from your commit that is currently being replayed.
This can be confusing because HEAD during a rebase does not point to your original branch — it points to the commit on the target branch (or the last successfully replayed commit). Keep this in mind when deciding which changes to keep.
Common Mistake: Running
git commitafter resolving a rebase conflict instead ofgit rebase --continue. This creates an extra commit that wasn’t part of the original branch, cluttering your history. The rebase command handles the commit for you.
Step 3: Edit the file to resolve the conflict. Remove the conflict markers and write the code you want:
function validate(input) {
if (!input) throw new Error('Input required');
return input.trim().length > 0;
}Step 4: Stage the resolved file:
git add src/utils.jsStep 5: Continue the rebase. Do not run git commit — the rebase handles the commit for you:
git rebase --continueGit will continue replaying the remaining commits. If another commit conflicts, repeat these steps.
Fix 2: Abort the Rebase
If you realize you should not have rebased, or the conflicts are too complex to deal with right now, abort the entire rebase:
git rebase --abortThis restores your branch to the exact state it was in before you started the rebase. No commits are lost or altered. Your branch, your working directory, and your index all go back to their pre-rebase state.
Use this when:
- You accidentally rebased onto the wrong branch.
- The conflicts are extensive and you need to rethink your approach.
- You decide that a merge is a better strategy than a rebase for this situation.
Fix 3: Skip a Commit During Rebase
If a particular commit has become irrelevant or its changes are already present in the base branch, you can skip it:
git rebase --skipThis drops the current commit entirely and moves on to replay the next one. The skipped commit will not appear in your rebased branch history.
Use this carefully. Skipping a commit means its changes are permanently excluded from your branch. This is appropriate when:
- The commit was cherry-picked or manually applied to the target branch already, so your copy is now a duplicate.
- The commit is a trivial change (like a whitespace fix) that was superseded by upstream changes.
- You intentionally want to drop this commit from your branch.
If you are unsure whether skipping is safe, abort the rebase instead and investigate the commit more carefully.
Fix 4: Resolve Repeated Conflicts with rerere
When you rebase a branch that has many commits, you may encounter the same logical conflict multiple times. For example, if commit B changes a function signature and commits C and D also reference that function, each replay can conflict in a similar way.
Git has a built-in feature called rerere (“reuse recorded resolution”) that memorizes how you resolve conflicts. If the same conflict pattern appears again, Git automatically applies your previous resolution.
Enable it globally:
git config --global rerere.enabled trueOnce enabled, Git records every conflict resolution you make. The next time the same conflict appears (during a rebase, merge, or cherry-pick), Git applies the recorded resolution automatically. You will see:
Resolved 'src/utils.js' using previous resolution.You still need to verify the resolution is correct, stage the file, and run git rebase --continue, but you do not have to manually edit the conflict markers again.
To clear a bad recorded resolution and re-resolve manually:
git checkout -m src/utils.jsThis re-creates the conflict markers so you can resolve the file from scratch. Your new resolution will replace the old recording.
Enabling rerere is especially valuable if you rebase frequently or maintain long-lived branches that you periodically rebase onto main.
Fix 5: Rebase Onto a Specific Commit
Sometimes you need to rebase only part of your branch, or rebase onto a commit that is not the tip of another branch. The --onto flag gives you precise control:
git rebase --onto <new-base> <old-base> <branch>Example 1: You branched feature off develop, but now you want to rebase it onto main instead:
git rebase --onto main develop featureThis takes all commits in feature that are not in develop and replays them onto main.
Example 2: You want to drop the first two commits of your branch and rebase the rest onto main. If your branch has commits A-B-C-D (where A is the oldest), and you want to drop A and B:
git rebase --onto main HEAD~2Conflicts during an --onto rebase are resolved the same way as a normal rebase: edit the files, git add, and git rebase --continue.
The --onto form is especially useful for moving branches between base branches or for surgically removing commits from a branch without interactive rebase.
Fix 6: Resolve Conflicts During Interactive Rebase
Interactive rebase (git rebase -i) lets you edit, reorder, squash, or drop commits. Conflicts can arise during interactive rebase for the same reasons as a normal rebase, plus a few additional scenarios:
git rebase -i mainThis opens an editor with a list of your commits:
pick 3a7b2c1 Add input validation
pick 5c1d8f3 Refactor error handler
pick 9e4a7b6 Update testsYou can change pick to other commands:
squash(ors): Combine this commit with the previous one. If the two commits modified the same lines differently, you will get a conflict.edit(ore): Pause the rebase at this commit so you can amend it. Changes you make while editing can conflict with later commits when the rebase continues.reword(orr): Change the commit message only. This does not cause conflicts.drop(ord): Remove the commit entirely. This can cause conflicts if later commits depend on changes introduced by the dropped commit.- Reordering lines: Moving commits to a different order can create conflicts if a later commit depends on an earlier one.
When conflicts occur during interactive rebase, the resolution workflow is the same:
# 1. Edit the conflicting files to resolve markers
# 2. Stage the resolved files
git add src/utils.js
# 3. Continue the rebase
git rebase --continueIf you reordered commits and the conflicts are intractable, abort and try a different ordering:
git rebase --abortFix 7: Force Push After Rebase
After rebasing a branch that was already pushed to a remote, your local branch and the remote branch have diverged. The commit hashes are different because rebase creates new commits. A normal git push will fail with a non-fast-forward error:
! [rejected] feature -> feature (non-fast-forward)You need to force push to update the remote branch with your rebased history. Use --force-with-lease instead of --force:
git push --force-with-lease origin feature--force-with-lease is safer than --force because it checks that the remote branch has not been updated by someone else since your last fetch. If a teammate pushed new commits to feature after your last fetch, the push is rejected, preventing you from overwriting their work.
Never force push to shared branches like main or develop without coordinating with your team. Force pushing rewrites the remote history, which can cause serious problems for anyone who based work on the old commits.
If you are working on a personal feature branch that no one else uses, force pushing after rebase is a normal and expected part of the workflow.
Rebase vs Merge: Choosing a Strategy
Not every situation calls for rebase. Here is when to use each:
Use rebase when:
- You want a clean, linear commit history without merge commits.
- You are updating a feature branch with the latest changes from
mainbefore opening a pull request. - You are working on a personal branch that nobody else has checked out.
- You want to clean up your commit history (squash, reorder, reword) before merging.
Use merge when:
- You are integrating a feature branch into
main(the final integration step). - Multiple people are working on the same branch. Rebasing a shared branch forces everyone else to deal with the rewritten history.
- You want to preserve the exact historical record of when branches were created and merged.
- The branch has been pushed and others have based work on it. Rebasing would change the commit hashes they depend on.
A common workflow combines both: rebase your feature branch onto main to catch up with upstream changes (keeping your branch’s history clean), then merge the feature branch into main with a merge commit (preserving the record of the integration).
The golden rule of rebase: Never rebase commits that have been pushed to a shared branch and that other people are working on. Rebase is for cleaning up your own local history before sharing it. If you have already shared the commits, coordinate with your team or use merge instead. If you end up in a detached HEAD state after a failed rebase, abort the rebase first to get back to your branch.
Still Not Working?
Same conflict keeps appearing on every commit
If you are resolving the same conflict over and over during a single rebase, this usually means multiple commits in your branch touch the same lines that were changed upstream. You have three options:
- Enable rerere (see Fix 4 above) so Git automatically applies your resolution after the first time.
- Squash your commits first. If your branch has many small commits that all touch the same area, squash them into fewer commits before rebasing onto the target branch. Fewer commits means fewer opportunities for the same conflict to appear:
# Squash your feature branch commits first
git rebase -i HEAD~5
# Then rebase onto main
git rebase main- Use merge instead. If you have a large branch with many commits and extensive conflicts, merging resolves everything in one step instead of per-commit.
Rebase got into a strange state
If your rebase seems stuck or in a broken state, check what Git thinks is happening:
ls .git/rebase-merge/
# or
ls .git/rebase-apply/If these directories exist, Git still considers a rebase to be in progress. You can abort cleanly with:
git rebase --abortIf that fails for some reason, you can manually clean up by removing the rebase state directory, but this is a last resort:
rm -rf .git/rebase-merge
rm -rf .git/rebase-applyCannot push after rebase
If git push fails after a successful rebase, you need to force push. See Fix 7 above. Make sure you understand whether the branch is shared before force pushing. If you are getting a Permission denied (publickey) error instead of a non-fast-forward rejection, the problem is your SSH configuration, not the rebase.
Rebase lost my commits
If you think rebase dropped or lost commits, they are almost certainly still recoverable. Git keeps a reflog of all recent HEAD positions:
git reflogFind the commit hash from before the rebase started (look for the entry that says rebase (start)). Then reset your branch back to that point:
git reset --hard <commit-hash-before-rebase>This undoes the entire rebase and restores your branch to its pre-rebase state.
Conflicts in files you did not change
During a rebase, you might see conflicts in files that you never directly edited. This happens when:
- One of your commits changes a file that was also changed upstream, but in a different area that Git cannot cleanly merge due to proximity.
- A commit you are replaying depends on a file that was renamed or moved upstream.
- Your branch was based on old code, and the context around your changes shifted enough that Git cannot find a clean place to apply them.
In these cases, look carefully at both sides of the conflict. Often the correct resolution is to accept the upstream version for the parts you did not write, while keeping your intentional changes.
Rebase works but tests fail afterward
If the rebase completes without conflicts but the code does not work, the problem is a semantic conflict. Git resolved the text-level merge correctly, but the combined code is logically broken. For example, upstream renamed a function that your commit still calls by the old name. Git does not detect this because the changes are in different files or on different lines.
After every rebase, build your project and run your tests before force pushing. Fix any issues with a new commit on top of the rebased branch.
Pro Tip: Before starting a long rebase, run
git stashto save any uncommitted work, thengit log --oneline main..HEADto see exactly how many commits will be replayed. If there are more than 10, consider squashing first to reduce the number of potential conflict points.
Related: Fix: fatal: not a git repository | Fix: git push rejected (non-fast-forward)
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Git "cannot lock ref" – Unable to Create Lock File
How to fix the Git error 'cannot lock ref: Unable to create .git/refs/heads/branch-name.lock' caused by stale lock files, case conflicts, packed-refs corruption, and concurrent operations.
Fix: fatal: remote origin already exists
How to fix the 'fatal: remote origin already exists' error in Git by updating the remote URL, removing and re-adding origin, managing multiple remotes, and handling forked repos.
Fix: Git LFS Smudge Filter Error
Resolve Git LFS smudge filter errors by installing Git LFS, fixing credentials, resetting LFS hooks, and handling bandwidth or storage quota issues.
Fix: Git Detached HEAD State – How to Reattach and Save Your Work
How to fix Git's detached HEAD state, reattach to a branch, and recover commits made while in detached HEAD.