merge-resolve
Resolve merge conflicts using the structural merge chain: mergiraf → rerere → kdiff3.
File IO delegation: This skill does NOT hold
Read,Edit,Write, orGlob. For per-file conflict inspection or manual edits, delegate to:
- cheez-read — inspect conflicted files, view conflict hunks, list directory contents
- cheez-search — locate conflict markers, search for related symbols across the tree
- cheez-write — apply resolved content with hash anchors
The bash-driven flows below (git, mergiraf, python scripts) handle the bulk of resolution. Drop into the cheez skills only when you need to inspect or rewrite a specific file.
Resolution Chain
The three tools form a cascade — each handles what the previous couldn't:
mergiraf runs automatically as a git merge driver. It parses all three file versions (base, ours, theirs) into Tree-sitter ASTs and merges structurally. Independent additions (imports, functions, struct fields) merge cleanly even when text-based merge would conflict. Falls back to text merge if parsing fails — never makes things worse.
rerere ("reuse recorded resolution") activates after mergiraf. If you manually resolved the same conflict before, rerere replays that resolution automatically. Especially valuable during long rebases where the same conflict recurs across commits.
kdiff3 is the manual fallback for conflicts neither tool could resolve. Launch with
git mergetool— it opens a 3-way diff view for human decision-making.
Protocol
1. Diagnose the Conflict State
Run the summary script first — it replaces the need for grep -n '<<<<<<<' and ad-hoc parsers:
python3 skills/merge-resolve/scripts/conflict-summary.py
Default output is terse and LLM-oriented: one metadata line per file, then minimally-framed hunks. Each hunk shows up to 5 lines of ours/theirs and 3 of base, capped to keep token cost low.
Flags:
--json— structured output for scripting--verbose— markdown-formatted human view (the previous default)--context N— context lines around each hunk (default 3)
If you need raw git info too:
git log --merge --oneline # What commits are involved?
2. Attempt Structural Resolution
For files where mergiraf is configured but conflicts remain (meaning the structural merge already ran and couldn't fully resolve), you can inspect what mergiraf produced vs text merge:
# Extract the 3-way inputs from git's stage slots
git show :1:<path> > /tmp/base # Stage 1 = common ancestor
git show :2:<path> > /tmp/ours # Stage 2 = current branch (HEAD)
git show :3:<path> > /tmp/theirs # Stage 3 = incoming branch
# Preview what mergiraf would produce (writes to -o, doesn't touch working copy)
mergiraf merge /tmp/base /tmp/ours /tmp/theirs -o /tmp/merged -p <path>
# Check if clean (no conflict markers)
grep -c '<<<<<<' /tmp/merged # 0 = clean
If the merged output has no conflict markers, mergiraf resolved it cleanly — the git merge driver may have fallen back to text merge due to a parse error or size limit. Apply the clean output:
cp /tmp/merged <path>
git add <path>
3. Batch Resolution
For repos with many conflicted files, use the batch script:
# Preview what can be resolved (no file changes; dry-run is the default)
python3 skills/merge-resolve/scripts/batch-resolve.py
# Apply all clean resolutions
python3 skills/merge-resolve/scripts/batch-resolve.py --apply
# Markdown-formatted output and mergiraf debug logs
python3 skills/merge-resolve/scripts/batch-resolve.py --verbose
The script extracts 3-way inputs for every conflicted file, runs mergiraf merge
on them, and reports which files resolved cleanly vs which need manual intervention.
4. Handle Remaining Conflicts
After structural resolution, for files that still have conflict markers:
Check rerere first:
git rerere status # Files with recorded resolutions
git rerere diff # Show what rerere would apply
If rerere has a resolution, it was already applied. If not, guide the user to manual resolution:
git mergetool # Opens kdiff3 for each conflicted file
# Or for a specific file:
git mergetool <path>
After manual resolution:
git add <resolved-files>
# Then continue the interrupted operation:
git merge --continue # or
git rebase --continue # or
git cherry-pick --continue
5. Debug Mergiraf
When mergiraf isn't resolving something you expect it to:
# Run with debug logging to see parse results and matching decisions
RUST_LOG=mergiraf=debug mergiraf merge /tmp/base /tmp/ours /tmp/theirs -o /tmp/merged -p <path> 2>&1
# Check if the file type is registered
mergiraf languages | grep <extension>
# Verify gitattributes
git check-attr merge -- <path>
# Should show: <path>: merge: mergiraf
Common issues:
- File not registered: Extension missing from
~/.gitattributes— regenerate after upgrade - Parse failure: Syntax error in one of the three versions — mergiraf falls back silently
- Size limit: Very large files (>1MB) may skip structural merge
6. Maintenance Commands
Regenerate gitattributes after mergiraf upgrade:
mergiraf languages --gitattributes > ~/.gitattributes
Manage rerere state:
git rerere status # What's currently being tracked
git rerere diff # Pending resolution diffs
git rerere forget <path> # Forget a bad resolution for one file
git rerere gc # Clean old entries
ls .git/rr-cache/ # Browse the resolution database
Scripts
Four scripts in skills/merge-resolve/scripts/ cover the common patterns:
| Script | Purpose | When to use |
|---|---|---|
conflict-summary.py |
Structured summary with line numbers + context | Always run first |
batch-resolve.py |
Run mergiraf merge on all conflicted files |
Supported langs with structural conflicts |
conflict-pick.py |
Choose ours/theirs per hunk | Shell, SQL, or formats not handled by mergiraf |
lockfile-resolve.py |
Take one side + regenerate lockfile | Cargo.lock, package-lock.json, etc. |
conflict-pick.py — for file types not handled by mergiraf:
# Take ours for all hunks
python3 skills/merge-resolve/scripts/conflict-pick.py hooks/session-start.sh --ours
# Take theirs for all hunks
python3 skills/merge-resolve/scripts/conflict-pick.py .gitignore --theirs
# Take ours only for hunks matching a pattern
python3 skills/merge-resolve/scripts/conflict-pick.py config.yaml --grep "timeout" --ours
lockfile-resolve.py — take theirs, regenerate:
# Auto-detect conflicted lockfiles and regenerate
python3 skills/merge-resolve/scripts/lockfile-resolve.py
# Preview
python3 skills/merge-resolve/scripts/lockfile-resolve.py --dry-run
Supports: Cargo.lock, package-lock.json, yarn.lock, pnpm-lock.yaml,
poetry.lock, Pipfile.lock, uv.lock, Gemfile.lock, go.sum.
Special Cases
Lockfiles
Lockfiles need special handling — textual or structural merge produces valid syntax
but potentially invalid dependency graphs. Use lockfile-resolve.py. The proven
pattern: take --theirs on the lockfile, then regenerate from the merged manifest.
Whitespace-Only Formatting Changes
If one branch ran a formatter while the other modified content, mergiraf may produce more conflicts because AST positions shifted. Resolution: run the formatter on the merged result after resolving conflicts.
Abort if Stuck
If the conflict state is unrecoverable:
git merge --abort # or
git rebase --abort # or
git cherry-pick --abort
What This Skill Doesn't Do
- Push or create PRs — hand off to the gh skill
- Run builds or tests — use appropriate build/test skills after resolving
- Commit resolved files — use the commit skill
- Architectural review of merge results — use age or code-review
- Abort the operation — presents abort as an option, user decides
- Read, edit, or search files directly — delegate to cheez-read / cheez-write / cheez-search
Gotchas
mergiraf solveflag confusion: use--stdout/-pfor preview, NOT--output- Markdown is supported by mergiraf but may need
.gitattributesregistration - Lockfile structural merge != valid lockfile — always regenerate after taking a side
- zdiff3 base markers: files may contain
|||||||sections — all scripts handle it - If you see conflicts in a supported file type, mergiraf-as-driver already ran