Skip to content

Romelium/mpatch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mpatch

CI Status Latest Release Crates.io License: MIT Downloads

mpatch is a context-aware patching utility designed to handle the "messy" reality of modern development. Unlike standard patch or git apply, which fail if line numbers or context don't match exactly, mpatch uses fuzzy matching to locate changes based on the surrounding code structure.

It is specifically engineered to apply diffs generated by LLMs (ChatGPT, Gemini, Claude, Copilot), which often hallucinate line numbers or provide slightly outdated context.


The Problem vs. The Solution

You ask an AI to refactor a function. It gives you a diff. However, you modified a comment in that function 5 minutes ago.

Standard patch: Fails. The context lines don't match byte-for-byte. mpatch: Succeeds. It sees the code structure is the same and applies the fix.

Original File (Modified Locally) AI-Generated Patch (Stale Context) mpatch Result
fn main() {
// I changed this comment
println!("Hello");
}
 fn main() {
// Original comment
- println!("Hello");
+ println!("World");
}
fn main() {
// I changed this comment
println!("World");
}

Key Features

  • 🧠 Fuzzy Matching: Uses a similarity algorithm to find the best location for a patch when exact matches fail. It handles stale context, whitespace changes, and minor code drift.
  • 🤖 Format Agnostic: Automatically detects and parses:
    • Markdown code blocks (standard AI output).
    • Raw Unified Diffs (git diff output).
  • ✨ Smart Indentation: Automatically adjusts the indentation of added lines to match the target file. Perfect for applying patches that were nested inside Markdown lists or have different tab/space styles.
  • 🛡️ Secure: Built-in path traversal protection prevents malicious patches from writing outside the target directory (../../etc/passwd).
  • ⚡ High Performance: Fuzzy searching is computationally expensive, so mpatch parallelizes the search across all CPU cores using rayon.
  • 🔍 Dry Run: Preview changes with --dry-run before touching your filesystem.

Installation

Option 1: Pre-compiled Binaries (Recommended)

We provide pre-compiled binaries for Windows, macOS, and Linux.

Using cargo-binstall (Fastest):

cargo binstall mpatch

Manual Download:

  1. Go to the Releases Page.
  2. Download the archive for your architecture (see table below).
  3. Extract and add to your PATH.
Platform Architecture Target Notes
macOS Universal universal-apple-darwin Best for Mac. Runs natively on M1/M2/M3 & Intel.
x64 x86_64-apple-darwin Older Intel Macs.
ARM64 aarch64-apple-darwin Apple Silicon (M1/M2/M3).
Windows x64 x86_64-pc-windows-msvc Standard 64-bit Windows.
ARM64 aarch64-pc-windows-msvc Surface Pro X, Parallels.
Linux x64 x86_64-unknown-linux-gnu Ubuntu, Debian, Fedora, etc.
x64 (Static) x86_64-unknown-linux-musl Alpine Linux, Docker containers.
ARM64 aarch64-unknown-linux-gnu Raspberry Pi 4/5, AWS Graviton.
ARM64 (Static) aarch64-unknown-linux-musl Alpine on ARM64.
ARMv7 armv7-unknown-linux-gnueabihf Older Raspberry Pi (2/3), IoT.
ARMv7 (Static) armv7-unknown-linux-musleabihf Static binaries for ARMv7.

Verifying Signatures

All release artifacts are signed with GPG. You can verify the integrity of the downloaded files using the .sig signature file and our public key (public.key), which is included in the release assets.

  1. Import the public key:

    gpg --import public.key
  2. Verify the archive:

    # Example for Linux x64
    gpg --verify mpatch-x86_64-unknown-linux-gnu-v1.3.5.tar.gz.sig mpatch-x86_64-unknown-linux-gnu-v1.3.5.tar.gz

Option 2: Build from Source

cargo install mpatch

CLI Usage

Basic Application

Apply a patch file (Markdown, Diff, or Conflict markers) to a target directory.

mpatch changes.md ./src

Preview Changes (Dry Run)

See exactly what will happen without modifying files.

mpatch --dry-run changes.md ./src

Adjusting Sensitivity

If mpatch is matching the wrong place, increase the strictness (default is 0.7). If it's failing to find a match, lower it.

# Stricter (needs 90% similarity)
mpatch --fuzz-factor 0.9 changes.md ./src

# Disable fuzzy matching (exact match only)
mpatch --fuzz-factor 0.0 changes.md ./src

Debugging

If a patch fails, generate a comprehensive debug report (includes file states, logs, and diffs) to analyze why.

mpatch -vvvv changes.md ./src
# Generates: mpatch-debug-report-[timestamp].md

Library Usage

mpatch is designed to be the patching engine for AI coding agents and tools. It exposes a robust Rust API.

Add to Cargo.toml:

[dependencies]
mpatch = "1.3.5"

1. Simple One-Shot (String to String)

Ideal for processing text in memory.

use mpatch::{patch_content_str, ApplyOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let original_code = "fn main() { println!(\"Old\"); }";

    // Input can be Markdown, Raw Diff, or Conflict Markers
    let patch_text = r#"
    ```diff
    --- a/main.rs
    +++ b/main.rs
    @@ -1 +1 @@
    -fn main() { println!("Old"); }
    +fn main() { println!("New"); }
    ```
    "#;

    let options = ApplyOptions::new(); // Default fuzz_factor: 0.7
    let new_code = patch_content_str(patch_text, Some(original_code), &options)?;

    assert_eq!(new_code, "fn main() { println!(\"New\"); }");
    Ok(())
}

2. Batch Application (File System)

Ideal for CLI tools applying multi-file patches.

use mpatch::{parse_auto, apply_patches_to_dir, ApplyOptions};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let diff_content = include_str!("../tests/fixtures/changes.md");

    // 1. Parse (automatically detects format)
    let patches = parse_auto(diff_content)?;

    // 2. Apply
    let target_dir = Path::new("./src");
    let options = ApplyOptions::new();

    let results = apply_patches_to_dir(&patches, target_dir, options);

    if results.all_succeeded() {
        println!("All patches applied successfully!");
    } else {
        // Inspect specific failures
        for (path, result) in results.hard_failures() {
            eprintln!("Failed to process {}: {}", path.display(), result);
        }
    }
    Ok(())
}

Supported Input Formats

mpatch automatically detects the format. You don't need to specify flags.

  1. Markdown Code Blocks: Standard output from LLMs. Supports ```diff, ```rust, or even generic blocks if they contain diff headers.
  2. Unified Diffs: Standard git diff or diff -u output.

Performance

Fuzzy matching is an $O(N \times M)$ operation. To ensure speed on large files:

  1. Heuristics: mpatch first attempts exact matches and "whitespace-insensitive" exact matches before falling back to fuzzy search.
  2. Anchoring: It scans for unique lines in the patch to narrow the search window.
  3. Parallelism: If a full scan is required, it uses Rayon to distribute the workload across all available CPU cores.

Benchmarks are available in benches/mpatch_bench.rs.


Contributing

Contributions are welcome!

  • Bug Reports: Please run mpatch -vvvv ... to generate a debug report and attach it to your issue.
  • Development:
    git clone https://github.com/romelium/mpatch.git
    cd mpatch
    cargo test

License

MIT License. See LICENSE for details.