Mpatch
mpatch is a contextual patching program/library tailored towards the "messy" world of modern software development. Different from standard patch and git apply, which require line number/context to be exact matches, mpatch utilizes fuzzy matching, looking for patches based on the context around the change in the code.
This tool was created to easily apply diffs produced by LLMs (ChatGPT, Gemini, Claude, Copilot), since they tend to hallucinate the exact line numbers or the context around the patch.
The Problem and the Solution
You ask an AI to modify some code. You get the diff. Except, the comment inside this code block is missing.
Standard patch: Won't work. The context isn't exact byte-for-byte match.
mpatch: Works! It notices that the code structure hasn't changed and applies the patch.
| Original File (Modified Locally) | AI-Generated Patch (Stale Context) | patch Result |
mpatch Result |
|---|---|---|---|
| fn main() { // Forgettable comment println!("Hello");} | fn main() {- println!("Hello");+ println!("World"); } | ❌ FailedHunk #1 FAILED at 1.1 out of 1 hunk FAILED | fn main() { // Forgettable comment println!("World");} |
Supported Input Formats
mpatch automatically detects the input format. Flags are not needed.
- Code Blocks in Markdown: The default output format for language models. Uses either
```diff,```rust, or other code blocks if they have diff headers. - Unified Diff: Default
git diffordiff -uformat. - Conflict Marker: Git-like conflict markers with
<<<<,====, and>>>>. Reminder: They lack file paths and are suitable for patching strings in memory.
Features
- 🧠 Fuzzy Matching: Runs a similarity algorithm to find the most suitable place to apply a patch if no exact matching is found. It supports stale context, whitespaces changes, and minor code differences.
- 🤖 Format Independent: Automatically recognizes and processes:
- Markdown diff code blocks (standard chat output format).
- Unified Diff (output from
git diffcommand).
- 📋 Clipboard Support: Input directly from your clipboard with
-cor--clipboard. - ✨ Smarter Indentation: Automatically indents added lines to be consistent with the target file. It perfectly applies the patch files that were initially indented in Markdown lists or use another tab/space indentation style.
- 🗑️ File Deletion: Automatically removes the target file if the output becomes empty after patching.
- 🛡️ Secure: Path traversal is automatically prevented, which means that no evil patch files can overwrite anything outside the target directory.
- ⚡ Fast: As fuzzy searching takes time,
mpatchsearches for matches using every CPU core viarayon. - 🔍 Dry Run: Preview what the tool would do with
--dry-run.
Installation
Option 1: Pre-compiled Binaries (Recommended)
We provide pre-compiled binaries for Windows, macOS, and Linux.
Using cargo-binstall (Fastest):
Manual Download:
- Go to the Releases Page.
- Download the archive for your architecture (see table below).
- 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
The integrity of all released binaries is verified with GPG. The .sig signature file along with our public key (public.key) will help you verify the authenticity of the downloaded files.
-
Import the public key:
-
Verify the archive:
# Example for Linux x64
Option 2: Build from Source
CLI Usage
Basic Application
Apply a patch file (Markdown, Diff, or Conflict markers) to a target directory.
From Clipboard
Apply a patch copied to your clipboard directly to a target directory.
Preview Changes (Dry Run)
See exactly what will happen without modifying files.
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)
# Disable fuzzy matching (exact match only)
Reversing a Patch
Undo a previously applied patch (swaps additions and deletions).
Debugging
If a patch fails, generate a comprehensive debug report (includes file states, logs, and diffs) to analyze why.
# 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:
[]
= "1.5.0"
1. Simple One-Shot (String to String)
Ideal for processing text in memory.
use ;
2. Batch Application (File System)
Ideal for CLI tools applying multi-file patches.
use ;
use Path;
3. Reversing Patches
Programmatically invert patches (additions become deletions and vice versa).
use ;
let diff_content = "--- a/file\n+++ b/file\n@@ -1 +1 @@\n-old\n+new";
let patches = parse_auto?;
let reversed = invert_patches;
// Now apply `reversed` to undo changes
4. Strict Apply-or-Fail Workflow
If you want to treat partial applications (where some hunks fail) as an error, use the try_ variants.
use ;
let original_content = "fn main() { println!(\"Old\"); }";
let diff_content = "--- a/main.rs\n+++ b/main.rs\n@@ -1 +1 @@\n-fn main() { println!(\"Old\"); }\n+fn main() { println!(\"New\"); }";
let patch = parse_single_patch?;
let options = exact;
// Returns an Err if any hunk fails to apply
match try_apply_patch_to_content
5. Creating Patches
You can also use mpatch to generate patches by comparing two strings.
use Patch;
let old_text = "fn main() { println!(\"Old\"); }";
let new_text = "fn main() { println!(\"New\"); }";
// Create a patch with 3 lines of context
let patch = from_texts.unwrap;
println!;
About the Conflict Markers Format
Though supported by mpatch, this file format (<<<<, ====, >>>>) does not include any file path information.
- Using
mpatchfrom CLI: If the provided file contains only conflict markers,mpatchwill try to patch a file calledpatch_target. - Using
mpatchas a library: The format can be used by thepatch_content_strfunction if your target file content is stored in memory.
In case of a multiple file patching or using mpatch from CLI, it is recommended to use the Unified Diffs format (with --- and +++).
Performance
Fuzzy match is $O(N \times M)$ operation. To ensure speed on large files:
- Heuristics: Before doing a fuzzy match,
mpatchtries to find both exact and "whitespace-insensitive" exact matches. - Anchoring:
mpatchtries to look for unique lines in the patch file to shrink the match range. - Parallelism: If a full scan is required, it uses Rayon to parallelize the workload.
Benchmark code can be found in benches/mpatch_bench.rs. To run, use cargo bench.
Contributing
Contributions are welcome!
- Bug Reports: Please run
mpatch -vvvv ...to generate a debug report and attach it to your issue. - Development:
License
MIT License. See LICENSE for details.