Introduction

Git rebase conflicts and aborted rebase errors occur when replaying commits onto a new base encounters conflicting changes, and the rebase operation must be paused for manual resolution or abandoned entirely. Rebase rewrites commit history by transplanting commits from one branch onto another, creating new commits with different parents. Unlike merge which creates a merge commit, rebase produces a linear history but requires resolving conflicts for each commit that touches the same lines as the new base. Common causes include commits modifying same lines as target branch, squashing commits with conflicting changes, editing commits that depend on later changes, upstream branch rewritten before rebase completed, file renames not detected during rebase, encoding or line-ending changes conflicting with content, and interrupted rebase due to power loss or forced termination. The fix requires understanding rebase mechanics, conflict resolution strategies, recovery procedures using reflog, and safe rebase workflows. This guide provides production-proven techniques for handling rebase conflicts across feature branch management, commit cleanup, and synchronization workflows.

Symptoms

  • CONFLICT (content): Merge conflict in filename.ext during rebase
  • error: could not apply <commit> - Commit message
  • Rebase failed: Could not apply <commit>
  • You are currently rebasing branch 'feature' on 'main'
  • Rebase aborted: returning to refs/heads/feature
  • Cannot rebase: You have unstaged changes
  • fatal: Needed a single revision
  • Rebase stuck at same commit after multiple attempts
  • git rebase --continue fails repeatedly
  • Git status shows rebase in progress

Common Causes

  • Same file lines modified in both branches
  • Commit depends on changes not yet replayed
  • File renamed or moved in target branch
  • Whitespace changes conflicting with content changes
  • Binary files cannot be auto-merged
  • Case-only filename changes on case-insensitive filesystem
  • Symlink target changed
  • Rebase interrupted (power loss, killed process)
  • Target branch rewritten by force push during rebase

Step-by-Step Fix

### 1. Understand rebase conflict state

Check rebase status:

```bash # Current rebase state git status

# Output during conflict: # interactive rebase in progress; onto main # Last command done (1 command, 0 updates and 1 failing command): # pick abc1234 Feature commit # # Could not apply abc1234... Feature commit # # Unmerged paths: # both modified: src/file.py # # no changes added to commit

# View rebase todo list cat .git/rebase-merge/git-rebase-todo

# Output: # pick def5678 Next commit # pick ghi9012 Another commit # # Rebase abc1234..ghi9012 onto abc1234 (3 commands)

# Current commit causing conflict cat .git/rebase-merge/stopped-sha

# Rebase metadata ls -la .git/rebase-merge/ # Contains: head-name, onto, msgnum, end, git-rebase-todo, etc. ```

View conflict details:

```bash # See all conflicted files git diff --name-only --diff-filter=U

# View conflict in specific file git diff --diff-filter=U src/file.py

# View conflict with common ancestor git diff --diff-filter=U --base src/file.py # Common ancestor git diff --diff-filter=U --ours src/file.py # Current branch (target) git diff --diff-filter=U --theirs src/file.py # Rebasing commit

# Show conflict markers in file cat src/file.py # <<<<<<< HEAD # Content from target branch (main) # ======= # Content from rebasing commit # >>>>>>> abc1234 Feature commit ```

### 2. Resolve rebase conflicts

Standard conflict resolution:

```bash # 1. Identify conflicted files git status

# 2. Edit files to resolve conflicts # Remove conflict markers (<<<<<<, ======, >>>>>>) # Keep desired changes from both sides

# 3. Mark as resolved git add src/file.py

# 4. Continue rebase git rebase --continue

# If another conflict, repeat steps 2-4 # If no more conflicts, rebase completes

# 5. Verify result git log --oneline -5 git diff main...feature ```

Using mergetool:

```bash # Configure merge tool git config --global merge.tool vscode git config --global mergetool.vscode.cmd "code --wait \$MERGED"

# Or use other tools git config --global merge.tool meld git config --global merge.tool p4merge git config --global merge.tool kdiff3

# Launch during rebase conflict git mergetool

# Tool opens with: # - LOCAL: Target branch version (main) # - REMOTE: Commit being rebased # - BASE: Common ancestor # - MERGED: Result file to save

# After resolving in tool, save and close # Then continue git rebase --continue ```

### 3. Use interactive rebase strategies

Interactive rebase options:

```bash # Start interactive rebase git rebase -i main

# Editor opens with todo list: # pick abc1234 First commit # pick def5678 Second commit # pick ghi9012 Third commit

# Change commands: # pick, p = use commit # reword, r = use commit, edit message # edit, e = use commit, stop for amending # squash, s = meld into previous commit # fixup, f = like squash, discard message # exec, x = run command # break, b = stop for manual work # drop, d = remove commit

# Example: squash related commits # pick abc1234 First commit # squash def5678 Add feature part 1 # squash ghi9012 Add feature part 2 # Result: Single commit with all changes

# Example: edit commit to fix mistake # pick abc1234 First commit # edit def5678 Second commit # Will stop here # pick ghi9012 Third commit

# When rebase stops: # - Make changes # - git add . # - git commit --amend # - git rebase --continue ```

Autosquash for easy squashing:

```bash # Enable autosquash git config --global rebase.autosquash true

# Create fixup commit git commit --fixup=abc1234 # Commits to be squashed into abc1234

# Or squash commit git commit --squash=def5678

# Then rebase git rebase -i --autosquash main

# Git automatically reorders and marks fixup/squash commits # No manual editing needed: # pick abc1234 Original commit # fixup 111111 fixup! Original commit # Auto-placed # pick def5678 Next commit ```

### 4. Recover from aborted rebase

Recover using reflog:

```bash # Rebase was aborted, need to recover git reflog

# Output: # abc1234 HEAD@{0}: rebase aborted: returning to refs/heads/feature # def5678 HEAD@{1}: rebase: pick: Second commit # ghi9012 HEAD@{2}: rebase: pick: First commit # jkl3456 HEAD@{3}: checkout: moving from main to feature

# Find the rebase state before abort # Look for "rebase: pick" entries

# Restart from specific commit git rebase --continue --onto main <commit-before-abort>

# Or reset to pre-abort state git reset --hard def5678 # The last successful rebase commit

# Then restart rebase git rebase --onto main <original-base> feature ```

Recover lost commits:

```bash # If rebase was aborted and commits lost git reflog --all

# Find the orphaned commits # Look for the commit hash before abort

# Create branch at lost commit git branch recovered-work <commit-hash>

# Or use fsck for older commits git fsck --lost-found

# Output: # dangling commit abc1234...

# Inspect and recover git show abc1234 git branch recovered abc1234 ```

### 5. Handle complex rebase scenarios

Rebase with file renames:

```bash # Scenario: File renamed in target branch # Branch A: file.py modified # Branch B (main): file.py renamed to newfile.py

# Rebase may not detect rename # CONFLICT: file.py (deleted by B, modified by A)

# Resolution: # 1. Get the modified content git checkout --theirs file.py > /tmp/modified.py

# 2. Move to new location mv /tmp/modified.py newfile.py

# 3. Stage both changes git add newfile.py git rm file.py

# 4. Continue git rebase --continue

# Help Git detect renames in future git config --global diff.renames true git config --global merge.renames true ```

Rebase with binary files:

```bash # Binary file conflict # CONFLICT (binary): image.png

# Options: # 1. Keep one version git checkout --ours image.png git add image.png

# 2. Take incoming version git checkout --theirs image.png git add image.png

# 3. Use external tool git mergetool image.png

# 4. For documents, extract and merge manually # (e.g., PowerPoint XML)

# Continue git rebase --continue ```

Rebase skip vs abort:

```bash # Skip current commit (loses that commit's changes) git rebase --skip

# Use when: # - Commit is no longer needed # - Changes already incorporated # - Commit causes unresolvable conflict

# Abort entire rebase (return to original state) git rebase --abort

# Use when: # - Too many conflicts # - Decided not to rebase # - Need to reconsider approach

# After abort: # - Branch returns to pre-rebase state # - All commits intact # - Can restart rebase later ```

### 6. Safe rebase workflow

Pre-rebase checklist:

```bash # 1. Ensure clean working tree git status # If dirty: commit or stash git stash

# 2. Fetch latest from remote git fetch origin

# 3. Verify current branch git branch # Make sure on correct branch

# 4. Check what will be rebased git log --oneline main..feature

# 5. Create backup branch git branch feature-backup

# 6. Start rebase git rebase -i main

# Or non-interactive git rebase main ```

Post-rebase verification:

```bash # 1. Check commit history git log --oneline -10

# 2. Verify no conflicts remain git status

# 3. Run tests # (Should be done before pushing)

# 4. Compare with backup git diff feature-backup

# 5. Push with force-with-lease (safe force push) git push --force-with-lease origin feature

# NOT git push --force (can overwrite others' work) ```

Team-safe rebase practices:

```bash # NEVER rebase shared/public branches # Only rebase your own feature branches

# Before rebasing feature branch: # 1. Check if others are working on it git log --oneline origin/feature..feature

# 2. If shared, use merge instead git merge main

# 3. If must rebase, notify team # "Rebasing feature branch, please pull --rebase after"

# Team members then: git pull --rebase origin feature ```

### 7. Debug rebase issues

Rebase with verbose output:

```bash # Verbose rebase GIT_TRACE=1 git rebase -i main 2>&1 | tee /tmp/rebase.log

# Check rebase state files cat .git/rebase-merge/msgnum # Current commit number cat .git/rebase-merge/end # Total commits cat .git/rebase-merge/onto # Target commit cat .git/rebase-merge/head-name # Original branch

# Debug specific commit git show abc1234 # See what commit changes git diff abc1234^ abc1234 # Full diff git diff main abc1234 # Vs target branch ```

Rebase with conflict markers visible:

```bash # Configure conflict marker style git config --global merge.conflictstyle diff3

# Shows: # <<<<<<< HEAD # Target branch content # ||||||| abc1234 # Common ancestor content # ======= # Rebasing commit content # >>>>>>> def5678

# Helps understand original intent ```

### 8. Advanced rebase techniques

Rebase exec for testing:

```bash # Run tests after each commit git rebase -i --exec "make test" main

# Editor shows: # pick abc1234 First commit # exec make test # pick def5678 Second commit # exec make test

# If test fails, rebase stops # Fix issue, then: # git rebase --continue ```

Rebase with rerere:

```bash # Enable rerere (reuse recorded resolution) git config --global rerere.enabled true

# First conflict resolution recorded # Same conflict later = auto-resolved

# View recorded resolutions git rerere status

# Share with team git add .git/rr-cache/ # (Commit rerere database)

# Clear old resolutions git rerere gc ```

Rebase preserve merges:

```bash # Preserve merge commits during rebase git rebase --rebase-merges main

# Creates todo list with merge structure # Preserves branch topology

# Or drop merges for linear history git rebase --no-merges main ```

Prevention

  • Rebase frequently on latest main to minimize conflicts
  • Use interactive rebase to organize commits before conflicts arise
  • Enable rerere to remember common resolutions
  • Create backup branch before starting rebase
  • Use autosquash for organizing fixup commits
  • Never rebase shared/public branches
  • Test after each commit during interactive rebase
  • Document rebase workflow for team consistency
  • **could not apply <commit>**: Commit failed during rebase
  • **error: conflict when rebasing**: Unresolved conflict
  • **refusing to merge unrelated histories**: Different root commits
  • **You are not currently on a branch**: Detached HEAD during rebase
  • **Cannot rebase: multiple possibilities**: Ambiguous rebase target