A smart, context-aware patch tool for the modern developer.
mpatch applies unified diffs to your codebase, but with a twist. Instead of relying on strict line numbers, it finds the correct location to apply changes based on the surrounding context. It's designed to work seamlessly with patches generated by AI, copied from pull requests, or stored in markdown files.
The primary motivation for mpatch comes from working with Large Language Models (LLMs).
When you ask an AI like ChatGPT, Claude, or Copilot to refactor code, it often provides the changes in a convenient markdown format with ```diff or ```patch blocks. However, you can't trust that the line numbers are correct. Sometimes, even the surrounding context lines aren't a perfect, character-for-character match to your current code. A standard patch command will often fail in these situations.
This is the core problem mpatch was built to solve.
It intelligently ignores line numbers and uses a fuzzy, context-based search to find where the patch should apply. This makes it highly resilient to the small inaccuracies common in AI-generated diffs, allowing you to apply them with confidence.
This same logic makes it perfect for other common developer scenarios where patches are less formal:
- Code Snippets: Using a diff copied from a GitHub comment, a blog post, or a team chat.
- Iterative Development: Applying a patch to a branch that has slightly diverged from where the patch was created.
- Markdown-Aware: Directly parses unified diffs from within
```diffor```patchcode blocks in any text or markdown file. - Context-Driven: Primarily finds patch locations by matching context lines, making it resilient to preceding file changes. It intelligently uses the
@@ ... @@line numbers as a hint to resolve ambiguity when the same context appears in multiple places. - Fuzzy Matching: If an exact context match isn't found,
mpatchuses a sophisticated similarity algorithm to find the best fuzzy match. This logic can handle cases where lines have been added or removed near the patch location, allowing patches to apply even when the surrounding context has moderately diverged. - Highly Performant: The most computationally intensive task—fuzzy searching—is parallelized to take full advantage of multi-core processors, ensuring fast performance even on large files.
- Safe & Secure: Includes a
--dry-runmode to preview changes and built-in protection against path traversal attacks. - Flexible: Handles multiple files and multiple hunks in a single pass. It correctly processes file creations, modifications, and deletions (by removing all content from a file).
- Informative Logging: Adjustable verbosity levels (
-v,-vv) to see exactly whatmpatchis doing.
While mpatch is a powerful CLI tool, it's also designed to be used as a library in your own Rust projects. The core logic is exposed through a simple and flexible API.
Add mpatch to your Cargo.toml:
cargo add mpatchHere's a basic example of how to parse a diff and apply it to a string in memory:
use mpatch::{parse_diffs, apply_patch_to_content, ApplyOptions};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Define the original content and the diff.
let original_content = "fn main() {\n println!(\"Hello, world!\");\n}\n";
let diff_content = r#"
A markdown file with a diff block.
```diff
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,3 @@
fn main() {
- println!("Hello, world!");
+ println!("Hello, mpatch!");
}
```
"#;
// 2. Parse the diff content to get a list of patches.
let patches = parse_diffs(diff_content)?;
let patch = &patches[0];
// 3. Apply the patch to the content in memory.
let options = ApplyOptions::default();
let result = apply_patch_to_content(patch, Some(original_content), &options);
// 4. The patch should apply cleanly.
assert!(result.report.all_applied_cleanly());
// 5. Verify the new content.
let expected_content = "fn main() {\n println!(\"Hello, mpatch!\");\n}\n";
assert_eq!(result.new_content, expected_content);
Ok(())
}For more advanced use cases, such as applying patches directly to the filesystem or iterating through hunks one by one, check out the library documentation on docs.rs.
For users with the Rust toolchain, cargo-binstall is the fastest way to install mpatch. It downloads pre-compiled binaries, avoiding a local build.
First, install cargo-binstall if you don't have it. Here are a few quick methods:
On Linux and macOS:
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bashOr, if you use Homebrew:
brew install cargo-binstallOn Windows (PowerShell):
Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").ContentOnce cargo-binstall is installed, you can install mpatch:
cargo binstall mpatchPre-compiled binaries for various platforms are available for direct download.
- Navigate to the GitHub Releases page.
- Find the latest release and download the archive that matches your system (e.g.,
.tar.gzfor Linux/macOS,.zipfor Windows). - Extract the
mpatchexecutable (mpatch.exeon Windows). - Move the executable to a directory in your system's
PATH(e.g.,/usr/local/binon Linux/macOS).
Binaries are available for the following targets:
| OS | Architecture | Target Triple |
|---|---|---|
| Linux | x86-64 | x86_64-unknown-linux-gnu |
| Linux | x86-64 | x86_64-unknown-linux-musl (static) |
| Linux | ARM64 | aarch64-unknown-linux-gnu |
| macOS | Intel | x86_64-apple-darwin |
| macOS | Apple Silicon | aarch64-apple-darwin |
| Windows | x86-64 | x86_64-pc-windows-msvc |
| Windows | x86 (32-bit) | i686-pc-windows-msvc |
| Windows | ARM64 | aarch64-pc-windows-msvc |
If you have the Rust toolchain installed, you can compile and install mpatch from the official package registry:
cargo install mpatchTo build the very latest development version or to contribute to the project:
# Install directly from the main branch of the repository
cargo install --git https://github.com/romelium/mpatch.git
# Or, to work on the code locally:
git clone https://github.com/romelium/mpatch.git
cd mpatch
cargo install --path .mpatch [OPTIONS] <INPUT_FILE> <TARGET_DIR>Before modifying any files, you can preview the exact changes using the -n or --dry-run flag. This is the safest way to start.
mpatch --dry-run changes.md my-project/This will produce a diff of the proposed changes for each file, printed directly to your terminal:
----- Proposed Changes for src/main.rs -----
--- a
+++ b
@@ -1,5 +1,5 @@
fn main() {
- // This is the original program
- println!("Hello, world!");
+ // This is the updated program
+ println!("Hello, mpatch!");
}
------------------------------------
DRY RUN completed. No files were modified.
Once you are confident in the proposed changes, run the command without --dry-run. Use -v for informational output.
mpatch -v changes.md my-project/You will see a confirmation log:
Found 1 patch operation(s) to perform.
Fuzzy matching enabled with threshold: 0.70
>>> Operation 1/1
Applying patch to: src/main.rs
Applying Hunk 1/1...
Successfully wrote changes to 'my-project/src/main.rs'
--- Summary ---
Successful operations: 1
Failed operations: 0
-n,--dry-run: Show what changes would be made without modifying any files.-f,--fuzz-factor <FACTOR>: Set the similarity threshold for fuzzy matching, from0.0(disabled) to1.0(exact match). Default is0.7.-v,--verbose: Increase logging output. Use-vfor info,-vvfor debug,-vvvfor trace, and-vvvvto generate a comprehensive debug report file.
If a patch doesn't apply as expected, the best first step is to increase the logging verbosity to understand what mpatch is doing.
- Run with
-v: This shows which files and hunks are being processed. - Run with
-vv: This provides detailed debug information, including why a hunk might have failed to apply (e.g., "ambiguous match", "context not found"). - Run with
-vvv: This enables trace-level logging, showing the fuzzy matching scores and every step of the decision-making process.
For complex issues, the easiest way to gather all necessary information for a bug report is to use the -vvvv flag.
mpatch -vvvv changes.md my-project/This command will:
- Print full trace logs to your terminal.
- Create a file named
mpatch-debug-report-[timestamp].mdin your current directory.
This single markdown file contains everything needed to reproduce the issue: the command you ran, system information, the full input patch file, the original content of all target files, and the complete trace log.
This project is licensed under MIT LICENSE
Contributions are welcome! Whether it's a bug report, a feature request, or a pull request, your input is valued.
When opening an issue, the best way to help us is to provide a debug report.
- Run your command again with the
-vvvvflag.mpatch -vvvv [YOUR_ARGS]
- This will create a
mpatch-debug-report-[timestamp].mdfile. - Create a new issue on GitHub.
- Drag and drop the generated
.mdfile into the issue description to attach it. - Add any additional comments about what you expected to happen versus what actually happened.
This self-contained report gives us all the context we need to investigate the problem efficiently.
- Fork the repository.
- Create a new branch for your feature or bug fix.
- Make your changes.
- Add tests for your changes in the
tests/directory. - Ensure all tests pass by running
cargo test. - Format your code with
cargo fmt. - Submit a pull request with a clear description of your changes.