# Mpatch
[](https://github.com/romelium/mpatch/actions/workflows/ci.yml)
[](https://github.com/romelium/mpatch/releases/latest)
[](https://crates.io/crates/mpatch)
[](https://opensource.org/licenses/MIT)
[](https://crates.io/crates/mpatch)
`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.
| <pre>fn main() {<br> // I changed this comment<br> println!("Hello");<br>}</pre> | <pre> fn main() {<br> // Original comment<br>- println!("Hello");<br>+ println!("World");<br> }</pre> | <pre>fn main() {<br> // I changed this comment<br> println!("World");<br>}</pre> |
---
## 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`](https://github.com/cargo-bins/cargo-binstall#installation) (Fastest):**
```bash
cargo binstall mpatch
```
**Manual Download:**
1. Go to the [**Releases Page**](https://github.com/romelium/mpatch/releases).
2. Download the archive for your architecture (see table below).
3. Extract and add to your `PATH`.
| **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:**
```bash
gpg --import public.key
```
2. **Verify the archive:**
```bash
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
```bash
cargo install mpatch
```
---
## CLI Usage
### Basic Application
Apply a patch file (Markdown, Diff, or Conflict markers) to a target directory.
```bash
mpatch changes.md ./src
```
### Preview Changes (Dry Run)
See exactly what will happen without modifying files.
```bash
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.
```bash
# 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.
```bash
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`:
```toml
[dependencies]
mpatch = "1.3.5"
```
### 1. Simple One-Shot (String to String)
Ideal for processing text in memory.
```rust
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.
```rust
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](https://github.com/rayon-rs/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:**
```bash
git clone https://github.com/romelium/mpatch.git
cd mpatch
cargo test
```
## License
MIT License. See [LICENSE](LICENSE) for details.