Skip to content

Conflict Resolution Engine

GitWand's core engine classifies merge conflicts into 10 patterns, scores each with a composite confidence metric, and auto-resolves the ones it's confident about — leaving the complex ones for human judgment.

Pipeline

Conflicted text → parseConflictMarkers → classifyConflict → resolveHunk → MergeResult
  1. Parse — Extract conflict markers from the file, producing ConflictHunk objects with base, ours, and theirs lines
  2. Classify — Evaluate patterns in the pattern registry (v1.4) in priority order; each pattern declares whether it requires diff3 (base available), diff2, or works on both
  3. Score — Compute a composite confidence score for the classification
  4. Resolve — Apply the appropriate resolution strategy (or mark as manual)
  5. Validate — Check the merged output for residual markers and syntax errors

Pattern Registry (v1.4)

In v1.4, the classifier was rewritten around a prioritised pattern registry. Each pattern implements a common interface:

typescript
interface ConflictPattern {
  type: ConflictType
  priority: number                          // smaller = evaluated first
  requires: 'diff3' | 'diff2' | 'both'      // base availability required
  detect(h: ClassifyInput): boolean
  confidence(h: ClassifyInput): ConfidenceScore
  explanation(h: ClassifyInput): string
  passReason(h: ClassifyInput): string
  failReason(h: ClassifyInput): string
}

Patterns are evaluated in priority order until one matches. The full evaluation trace (passed/failed + reason for each step) lives in the DecisionTrace and is available via the --verbose CLI flag or the gitwand_explain_hunk MCP tool.

The 10 Conflict Types

same_change

Both sides made identical changes from the base. Resolution: use either side (they're the same).

one_side_change

Only one side modified the base; the other kept it unchanged. Resolution: take the modified side.

delete_no_change

One side deleted the block while the other left it unchanged. Resolution: accept the deletion.

non_overlapping

Both sides made changes, but at different locations within the block. Resolution: merge both changes using LCS (Longest Common Subsequence).

whitespace_only

The only differences are whitespace (indentation, trailing spaces, line endings). Resolution: prefer ours (preserve local formatting).

reorder_only (v1.4)

Both sides contain the same lines as the base, but in a different order — a pure permutation with no content changes. Common with import sorters, alphabetised keys, or table-of-contents updates. Resolution: take either side (content is identical).

insertion_at_boundary (v1.4)

Both sides only insert new lines around an intact base — no modifications to existing content. Resolution: concatenate both insertions, preserving the original base lines between them.

value_only_change

Same structure, but volatile values differ — hashes, version numbers, timestamps, integrity fields. Common in lockfiles and build outputs. Resolution: prefer theirs (incoming values are newer).

generated_file

The file is auto-generated (lockfiles, minified bundles, build manifests). Detected by filename patterns. Resolution: prefer theirs (the file will be regenerated).

Detected patterns: package-lock.json, yarn.lock, pnpm-lock.yaml, composer.lock, Gemfile.lock, Cargo.lock, .min.js, .min.css, dist/, build/manifest.json, .bundle.js, .bundle.css

complex

A real conflict that requires human judgment. GitWand does not auto-resolve these.

Composite Confidence Scoring

Each classification produces a score from 0 to 100 across three dimensions:

DimensionRangeMeaning
typeClassification0–100How certain is the conflict type detection
dataRisk0–100Risk of data loss (0 = safe, 100 = high risk)
scopeImpact0–100Impact of the change size (0 = small, 100 = large)

Formula:

score = typeClassification − dataRisk × 0.4 − scopeImpact × 0.15

Score-to-label mapping:

ScoreLabel
≥ 92certain
≥ 68high
≥ 44medium
< 44low

The score also includes boosters (factors that increased confidence) and penalties (cautionary factors) for transparency.

Decision Traces

Every classification produces a DecisionTrace showing which patterns were evaluated, which passed, and why the final type was selected. This is useful for debugging unexpected classifications.

typescript
interface DecisionTrace {
  steps: Array<{
    type: ConflictType
    passed: boolean
    reason: string
  }>
  selected: ConflictType
  summary: string
  hasBase: boolean
}

Use explainOnly: true in the options to get traces without writing any files.

Resolution Policies

The default resolution strategy can be overridden per-project with a .gitwandrc configuration file.

Default behavior by type:

TypeResolutionRationale
same_changeEither sideIdentical changes
one_side_changeModified sidePreserve the change
delete_no_changeAccept deletionIntentional removal
non_overlappingLCS mergeBoth changes coexist
whitespace_onlyOursPreserve local formatting
reorder_onlyEither sideSame content, different order
insertion_at_boundaryMerge bothIndependent additions around intact base
value_only_changeTheirsIncoming values are newer
generated_fileTheirsWill be regenerated
complexNo auto-resolutionToo risky

Format-Aware Resolvers

Beyond the generic text-based resolution, GitWand includes specialized resolvers for structured file formats:

  • JSON / JSONC — Recursive key-by-key merging, preserving structure
  • Markdown — Section-aware merging with frontmatter extraction
  • YAML — Structure-aware merging
  • TypeScript/JavaScript imports — Import statement deduplication
  • Vue SFC — Per-block (<template>, <script>, <style>) resolution
  • CSS — Rule-level merging
  • Lockfiles — Specialized handlers for npm, Yarn, and pnpm lockfiles

Format detection is automatic based on file extension. Format-aware resolvers are tried first; if they can't handle the conflict, the generic resolver takes over.

Post-Merge Validation

After resolution, GitWand validates the output:

  • Residual markers — Checks that no <<<<<<<, =======, or >>>>>>> markers remain
  • Syntax errors — For structured formats (JSON, YAML), validates the output parses correctly
typescript
interface ValidationResult {
  hasResidualMarkers: boolean
  residualMarkerLines: number[]
  syntaxError: string | null
  isValid: boolean
}

Released under the MIT License.