Skip to content

A rules-based JSON conflict resolver that parses Git conflict markers, reconstructs ours/theirs, and merges with deterministic strategies — beyond line-based merges.

License

Notifications You must be signed in to change notification settings

react18-tools/git-json-resolver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Git Json Resolver

Git JSON Resolver Banner

test codecov Version Downloads npm bundle size

A Git-aware conflict resolver for JSON-first structured data.

Why?

  • Git merge conflicts in structured files (JSON, YAML, XML, TOML) are painful.
  • Manual resolution is error-prone, time-consuming, and breaks CI/CD pipelines.
  • git-json-resolver automates conflict handling with configurable strategies.
  • Non-JSON formats are internally normalized to JSON → resolved → converted back.

Features

  • Primary focus on JSON (first-class support)
  • 🔄 YAML, XML, TOML, JSON5 supported via conversion
  • 🧩 Rule-based strategies with path/pattern matching
  • 📁 Multiple file parallel processing with include/exclude patterns
  • 🔌 Pluggable matcher abstraction (picomatch, micromatch, or custom)
  • 🛠️ CLI and programmatic API support
  • 📝 Conflict sidecar files for unresolved conflicts
  • 🔄 Backup and restore functionality
  • 📊 Configurable logging (memory or file-based)
  • 🔀 Git merge driver support for seamless Git integration
  • 🔧 Plugin system for custom strategies with JSON config support

Installation

pnpm add git-json-resolver

or

npm install git-json-resolver

or

yarn add git-json-resolver

Quick Start

CLI Usage

# Initialize config file npx git-json-resolver --init # Run with default config npx git-json-resolver # Run with options npx git-json-resolver --include "**/*.json" --debug --sidecar # Restore from backups npx git-json-resolver --restore .merge-backups

Git Integration

Add a custom merge driver to your Git config:

git config merge.json-resolver.name "Custom JSON merge driver" git config merge.json-resolver.driver "npx git-json-resolver %A %O %B"

Update .gitattributes to use it for JSON files:

*.json merge=json-resolver *.yaml merge=json-resolver *.yml merge=json-resolver *.toml merge=json-resolver *.xml merge=json-resolver

How it works:

  • Git automatically calls the merge driver during conflicts
  • Uses same configuration and strategies as CLI mode
  • Supports 3-way merge (ours, base, theirs)
  • Returns proper exit codes (0 = success, 1 = conflicts)

Configuration

Programmatic API

import { resolveConflicts } from "git-json-resolver"; await resolveConflicts({ defaultStrategy: ["merge", "ours"], rules: { "dependencies.*": ["ours"], version: ["theirs!"], // ! marks as important "scripts.build": ["skip"], }, include: ["**/*.json", "**/*.yaml"], exclude: ["**/node_modules/**"], matcher: "picomatch", debug: true, writeConflictSidecar: true, backupDir: ".merge-backups", });

Config File

JavaScript Config (git-json-resolver.config.js)

module.exports = { defaultStrategy: ["merge", "ours"], rules: { // Exact path matching "package.json": { version: ["theirs!"], dependencies: ["ours"], }, // Pattern matching "*.config.json": { "*": ["merge"], }, }, // Alternative: byStrategy format byStrategy: { ours: ["dependencies.*", "devDependencies.*"], "theirs!": ["version", "name"], }, include: ["**/*.json", "**/*.yaml", "**/*.yml"], exclude: ["**/node_modules/**", "**/dist/**"], matcher: "picomatch", debug: false, writeConflictSidecar: false, loggerConfig: { mode: "memory", // or "stream" logDir: "logs", levels: { stdout: ["warn", "error"], file: ["info", "warn", "error"], }, }, };

JSON Config (git-json-resolver.config.json) - ⚠️ Experimental

{ "$schema": "https://cdn.jsdelivr.net/npm/git-json-resolver@latest/schema/config.schema.json", "defaultStrategy": ["merge", "ours"], "plugins": ["my-plugin"], "pluginConfig": { "my-plugin": { "option": "value" } }, "rules": { "package.json": { "version": ["semantic-version", "theirs"], "dependencies": ["ours"] } }, "byStrategy": { "ours": ["dependencies.*", "devDependencies.*"], "theirs!": ["version", "name"] } }

⚠️ JSON Config Limitations:

  • No TypeScript intellisense for plugin strategies
  • Limited validation for custom strategy names
  • No compile-time type checking
  • Recommended: Use .js or .ts config for better developer experience

Supported Strategies

  • merge → deep merge objects/arrays where possible
  • ours → take current branch value
  • theirs → take incoming branch value
  • base → revert to common ancestor
  • skip → leave unresolved (creates conflict entry)
  • drop → remove the field entirely
  • non-empty → prefer non-empty value (ours > theirs > base)
  • update → update with theirs if field exists in ours
  • concat → concatenate arrays from both sides
  • unique → merge arrays and remove duplicates
  • custom → user-defined resolver functions

Strategy Priority

  • Strategies marked with ! (important) are applied first
  • Multiple strategies can be specified as fallbacks
  • Custom strategies can be defined via customStrategies config

Supported Formats

  • JSON (native)
  • JSON5 → via json5 peer dependency
  • YAML → via yaml peer dependency
  • TOML → via smol-toml peer dependency
  • XML → via fast-xml-parser peer dependency

All non-JSON formats are converted to JSON → resolved → converted back to original format.

CLI Options

# File patterns --include "**/*.json,**/*.yaml" # Comma-separated patterns --exclude "**/node_modules/**" # Exclusion patterns # Matcher selection --matcher picomatch # picomatch, micromatch, or custom # Debug and logging --debug # Enable verbose logging --sidecar # Write conflict sidecar files # Utilities --init # Create starter config file --restore .merge-backups # Restore from backup directory

Architecture

  • Modular design: Separate concerns (parsing, merging, serialization)
  • Reusable utilities: Common merge logic extracted for maintainability
  • Optimized bundle: Constants over enums for better minification
  • Comprehensive testing: Full test coverage with vitest
  • Type-safe: Full TypeScript support with proper type inference

Advanced Features

Pattern Matching

  • Exact paths: "package.json", "src.config.database.host"
  • Field matching: "[version]" → matches any version field
  • Glob patterns: "dependencies.*", "**.config.**"
  • Wildcards: "*.json", "src/**/*.config.js"

Plugin System

For TypeScript/JavaScript configs, you can use either approach:

1. Direct Import (Recommended)

import { strategies } from "my-plugin"; // or import { semanticVersion, timestampLatest } from "my-plugin"; import { resolveConflicts } from "git-json-resolver"; await resolveConflicts({ customStrategies: { ...strategies, // or "semantic-version": semanticVersion, }, rules: { version: ["semantic-version", "theirs"], }, });

2. Dynamic Loading

// Also works in .js/.ts configs const config = { plugins: ["my-plugin"], pluginConfig: { "my-plugin": { option: "value" }, }, rules: { version: ["semantic-version", "theirs"], }, };

3. JSON Config (Dynamic Loading Only)

{ "plugins": ["my-plugin"], "pluginConfig": { "my-plugin": { "option": "value" } }, "rules": { "version": ["semantic-version", "theirs"] } }

⚠️ If plugin doesn't provide global types:

import type { Config } from "git-json-resolver"; const config: Config<AllStrategies | "semantic-version" | "timestamp-latest"> = { // ... your config };

Creating a Plugin

import { StrategyPlugin, StrategyStatus, StrategyFn } from "git-json-resolver"; // Augment types for TypeScript support declare module "git-json-resolver" { interface PluginStrategies { "semantic-version": string; "timestamp-latest": string; } } // Individual strategy functions (can be imported directly) export const semanticVersion: StrategyFn = ({ ours, theirs }) => { if (isNewerVersion(theirs, ours)) { return { status: StrategyStatus.OK, value: theirs }; } return { status: StrategyStatus.CONTINUE }; }; export const timestampLatest: StrategyFn = ({ ours, theirs }) => { const oursTime = new Date(ours as string).getTime(); const theirsTime = new Date(theirs as string).getTime(); return { status: StrategyStatus.OK, value: oursTime > theirsTime ? ours : theirs, }; }; // Export strategies object for direct import export const strategies = { "semantic-version": semanticVersion, "timestamp-latest": timestampLatest, }; // Plugin interface for dynamic loading (object-based) const plugin: StrategyPlugin = { strategies, init: async config => { console.log("Plugin initialized with:", config); }, }; export default plugin; // Alternative: Function-based plugin (NEW) export default async function createPlugin(config?: any): Promise<StrategyPlugin> { // Initialize with config if needed console.log("Plugin initialized with:", config); return { strategies, init: async initConfig => { // Additional initialization if needed }, }; }

Custom Strategies (Inline)

import { StrategyStatus } from "git-json-resolver"; const config = { customStrategies: { "semantic-version": ({ ours, theirs }) => { // Custom logic for semantic version resolution if (isNewerVersion(theirs, ours)) { return { status: StrategyStatus.OK, value: theirs }; } return { status: StrategyStatus.CONTINUE }; }, }, rules: { version: ["semantic-version", "theirs"], }, };

Logging & Debugging

  • Memory mode: Fast, in-memory logging
  • Stream mode: File-based logging for large operations
  • Per-file logs: Separate log files for each processed file
  • Debug mode: Detailed conflict information and strategy traces

Plugin Development

See PLUGIN_GUIDE.md for detailed plugin development documentation.

Contributing

Contributions welcome 🙌

  • Fork, branch, PR — with tests (vitest required)
  • Docs live in libDocs/ (tutorials, guides, deep dives)
  • API reference generated into docs/ via TypeDoc

License

This library is licensed under the MPL-2.0 open-source license.

Please enroll in our courses or sponsor our work.


with 💖 by Mayank Kumar Chaudhari

About

A rules-based JSON conflict resolver that parses Git conflict markers, reconstructs ours/theirs, and merges with deterministic strategies — beyond line-based merges.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  
  •  

Packages

No packages published