Fix: Git Merge Conflict Loop

Error Message:

"Conflict in file X. Attempting to resolve..." "Merge failed. Retrying..." "CONFLICT (content): Merge conflict in src/components/Header.tsx" (The same conflict appears 5+ times without resolution)

What This Error Means

Claude Cowork detected a merge conflict, attempted to resolve it by editing the file, committed the result, and then hit another conflict — either in the same file or a different one. Instead of stopping and asking for help, it loops: resolve, commit, conflict, resolve, commit, conflict. Each iteration can introduce new syntax errors or partial edits, making the repo progressively worse.

Root Cause Analysis

The core problem is that Claude resolves conflicts textually, not semantically. It sees the <<<<<<< HEAD / ======= / >>>>>>> markers, reads both sides, and picks what looks reasonable — but "looks reasonable" isn't the same as "compiles and preserves both branches' intent." A conflict in an import block might get resolved by keeping one side's imports, silently dropping a dependency the other branch added. A conflict in a function body might get merged into invalid syntax that combines two incompatible code paths.

The loop compounds because each failed merge attempt leaves the working tree in a half-resolved state. Claude's next attempt reads that already-corrupted file as the new baseline, tries to fix it, and introduces further drift. After three or four iterations, the file bears little resemblance to either original branch, and the conflict markers themselves may be malformed (missing closing >>>>>>>, nested markers from partial edits).

A second trigger is when Claude tries to resolve conflicts across multiple files simultaneously. It fixes file A, commits, then discovers file B also conflicts. It fixes file B, commits, but the commit on file A triggered a rebase interaction that re-conflicts file A. This ping-pong can run indefinitely.

Real-World Scenario

You ask Claude to merge a feature/auth-refactor branch into main. The branch reorganized the authentication middleware and renamed getUser() to fetchUser(). Meanwhile, main added a new call site for getUser() in a route handler. Claude resolves the conflict in the route handler by keeping main's version (which calls getUser()), but resolves the middleware conflict by keeping feature/auth-refactor's version (which renamed it to fetchUser()). The merged code now calls a function that doesn't exist. Claude tries to fix the resulting TypeScript error, edits the file, commits — and triggers a new conflict because the rebase replay is still in progress. The loop begins.

Step-by-Step Fix

Step 1: Break the loop immediately

Stop the agent before it does more damage:

  • CLI: Press Ctrl+C
  • UI: Click Stop

Do not let it "try one more time." Each iteration worsens the state.

Step 2: Assess the damage

Check what state the repo is in:

git status
git log --oneline -10

If you're mid-merge or mid-rebase, you'll see unmerged paths. If Claude committed garbage, you'll see recent commits with messages like "resolve merge conflict" repeated.

Step 3: Abort and reset to a clean state

The safest path is to abort the in-progress operation and get back to a known-good commit:

# If mid-merge:
git merge --abort

# If mid-rebase:
git rebase --abort

# If Claude already committed broken merges, reset to before them:
git reset --hard HEAD~5   # adjust the number based on git log output

Verify you're clean:

git status
# Should show: "nothing to commit, working tree clean"

Step 4: Resolve conflicts manually (the reliable path)

Open each conflicted file in your IDE and use its merge tool. VS Code, JetBrains, and Vim all have three-way merge viewers that show both branches side by side. Pick sides deliberately — don't let an auto-resolver guess.

Once you've resolved all files:

git add <resolved-files>
git commit -m "merge feature/auth-refactor: manually resolved conflicts"

Then tell Claude:

"I manually resolved all merge conflicts and committed. 
The merge is complete. Do not attempt to re-merge."

Step 5: If you want Claude to retry with guidance

If the conflict is simple enough to trust Claude with a second attempt, give it an explicit strategy:

"Merge feature-branch into main. For any conflict in src/auth/, 
favor 'theirs' (the feature branch). For conflicts in src/routes/, 
favor 'ours' (main). Resolve one file at a time and show me each 
resolution before committing."

Common Pitfalls

  • Letting the loop run "just a few more times." It won't converge. Each iteration drifts further from either original branch. Stop at the first sign of a loop.
  • Resolving conflicts in the wrong order during a rebase. During a rebase, each commit is replayed separately. Resolving a conflict for commit N doesn't prevent the same file from conflicting again at commit N+1. This is normal — resolve each one as it comes.
  • Committing with conflict markers still in the file. Always search for <<<<<<< before committing. A single leftover marker breaks the build.
  • Trusting Claude to understand semantic conflicts. Claude can resolve textual conflicts (which lines to keep) but often misses semantic ones (a renamed function called by the other branch). Always review auth, routing, and API-surface conflicts yourself.

Prevention Checklist

  • Keep branches small. A 3-file branch merges cleanly; a 50-file branch conflicts on everything.
  • Run git pull --rebase origin main at the start of every session to stay current.
  • Merge or rebase frequently — don't let a feature branch diverge for weeks.
  • When asking Claude to merge, specify a conflict resolution strategy upfront ("favor theirs" / "favor ours" / "ask me for each file").
  • After any merge, run the test suite and type-checker before trusting the result.
  • If a merge involves renamed functions or moved files, resolve those conflicts manually — auto-resolution misses call-site updates.