Description
Conflict resolution (both the manual take-side flow and the AI resolution flow) currently handles only a subset of Git conflict types well. The common content conflict (UU modified-by-both) is fully supported, but many other unmerged states are either skipped, silently dropped, or surfaced as "unsupported." We should audit and fill in handling so that every conflict type a rebase/merge/cherry-pick/revert can produce has a sensible, predictable resolution path.
Current state
Manual take-side — classifyConflictAction (packages/git/src/utils/conflictResolution.utils.ts):
- Handles
UU, AA, UD, DU, DD via take-current / take-incoming / delete.
AU + take-incoming and UA + take-current are classified unsupported — single-file UI hides them; bulk resolve reports them as skipped.
- No special handling for renames, mode-only (exec-bit) changes, file/directory clashes, or binary/encoded files beyond "take a whole side."
AI resolution — @gitkraken/conflict-tools via src/plus/coretools/conflict/integration.ts:
ConflictType is only 'text' | 'delete-modify'.
extractConflict returns null for anything without parseable text markers (binary, symlink, add/add without markers, mode-only, encoded). Those files are recorded as skipped with reason 'no-markers' — i.e. the AI flow quietly leaves them for the user with no guidance.
Conflict types to support / verify
| Scenario |
Unmerged status |
Manual take-side |
AI flow |
Notes |
| Modified by both (content) |
UU |
✅ |
✅ text |
Baseline — works |
| Overlapping hunks |
UU |
✅ |
✅ text |
Verify multi-hunk |
| Add/Add (both added) |
AA |
✅ take-side |
⚠️ skipped (no-markers) |
AI should diff/merge the two added versions |
| Modify/Delete |
UD |
✅ |
✅ delete-modify |
Verify keep-vs-delete UX |
| Delete/Modify |
DU |
✅ |
✅ delete-modify |
Verify keep-vs-delete UX |
| Delete/Delete |
DD |
✅ delete |
⚠️ skipped |
Trivial but confirm |
| Add by us / Add by them |
AU / UA |
⚠️ one side unsupported |
⚠️ skipped |
Clearer UX than a silent skip when the requested side is absent |
| Rename/Rename (diverging targets) |
— |
❌ |
❌ |
No rename-aware handling |
| Rename/Delete |
— |
❌ |
❌ |
No rename-aware handling |
| Rename + Modify |
— |
❌ |
❌ |
Content edit on a renamed file |
| File/Directory clash (obstacle) |
— |
❌ |
❌ |
One side file, other side directory |
| Mode-only change (exec bit) |
— |
⚠️ |
⚠️ skipped |
Take-side may not preserve/choose mode |
| Binary file |
— |
⚠️ take-side |
⚠️ skipped |
No diff UI; AI can't parse |
| Symlink (divergent targets) |
— |
⚠️ |
⚠️ skipped |
AI can't parse |
| Encoded / UTF-16 (binary-treated) |
— |
⚠️ |
⚠️ skipped |
No marker parsing |
| Empty after rebase |
— |
❔ |
❔ |
Edge case — verify it doesn't error |
(✅ supported · ⚠️ partial/silent-skip · ❌ no handling · ❔ unverified)
Goals / acceptance criteria
- Every unmerged status and conflict scenario above has a defined, predictable outcome in both the manual and AI flows — resolved, or explicitly surfaced with an actionable next step (never silently dropped).
- "Unsupported" / "no-markers" cases present clear UI (e.g. "this is a binary/symlink/mode conflict — choose a side" or "rename conflict — pick a target") instead of disappearing from the results.
- Renames, mode changes, file/dir clashes, binary, symlink, and encoded files each get appropriate take-side (and where feasible AI) handling.
- Verified against the test branches below.
Test references
Internal conflict test branches live in ~/dev/test/dummy-repo-private. Rebasing main onto each tier produces the scenarios in rebase-conflicts/:
origin/rebase-conflict-test-base → common ancestor (ours-side changes)
origin/rebase-conflict-test-core → 8 core scenarios: both-modify, overlap, both-added, modify-vs-delete, delete-vs-modify, rename-rename, rename-vs-delete, file-vs-dir
origin/rebase-conflict-test-extended → core + mode + binary + symlink
origin/rebase-conflict-test-all → extended + rename-modify + UTF-16 + empty-after-rebase
Scenario files: rebase-conflicts/{core-both-modify,core-overlap,core-both-added,core-delete-vs-modify,core-modify-vs-delete,core-rename-vs-delete,theirs-renamed,all-rename-modify}.txt, core-obstacle/nested.txt, ext-mode.sh, ext-binary.png, ext-symlink, all-utf16.txt, all-empty-after-rebase.txt.
Also: ianhattendorf/conflict/multi-file/{base,left,right} for multi-file conflicts.
Description
Conflict resolution (both the manual take-side flow and the AI resolution flow) currently handles only a subset of Git conflict types well. The common content conflict (
UUmodified-by-both) is fully supported, but many other unmerged states are either skipped, silently dropped, or surfaced as "unsupported." We should audit and fill in handling so that every conflict type a rebase/merge/cherry-pick/revert can produce has a sensible, predictable resolution path.Current state
Manual take-side —
classifyConflictAction(packages/git/src/utils/conflictResolution.utils.ts):UU,AA,UD,DU,DDvia take-current / take-incoming / delete.AU+ take-incoming andUA+ take-current are classifiedunsupported— single-file UI hides them; bulk resolve reports them as skipped.AI resolution —
@gitkraken/conflict-toolsviasrc/plus/coretools/conflict/integration.ts:ConflictTypeis only'text' | 'delete-modify'.extractConflictreturnsnullfor anything without parseable text markers (binary, symlink, add/add without markers, mode-only, encoded). Those files are recorded asskippedwith reason'no-markers'— i.e. the AI flow quietly leaves them for the user with no guidance.Conflict types to support / verify
UUUUAAUDDUDDAU/UAunsupported(✅ supported ·⚠️ partial/silent-skip · ❌ no handling · ❔ unverified)
Goals / acceptance criteria
Test references
Internal conflict test branches live in
~/dev/test/dummy-repo-private. Rebasingmainonto each tier produces the scenarios inrebase-conflicts/:origin/rebase-conflict-test-base→ common ancestor (ours-side changes)origin/rebase-conflict-test-core→ 8 core scenarios: both-modify, overlap, both-added, modify-vs-delete, delete-vs-modify, rename-rename, rename-vs-delete, file-vs-dirorigin/rebase-conflict-test-extended→ core + mode + binary + symlinkorigin/rebase-conflict-test-all→ extended + rename-modify + UTF-16 + empty-after-rebaseScenario files:
rebase-conflicts/{core-both-modify,core-overlap,core-both-added,core-delete-vs-modify,core-modify-vs-delete,core-rename-vs-delete,theirs-renamed,all-rename-modify}.txt,core-obstacle/nested.txt,ext-mode.sh,ext-binary.png,ext-symlink,all-utf16.txt,all-empty-after-rebase.txt.Also:
ianhattendorf/conflict/multi-file/{base,left,right}for multi-file conflicts.