Expand description
A smart, context-aware patch tool that applies diffs using fuzzy matching.
mpatch
is designed to apply unified diffs to a codebase, but with a key
difference from the standard patch
command: it doesn’t rely on strict line
numbers. Instead, it finds the correct location to apply changes by searching
for the surrounding context lines.
This makes it highly resilient to patches that are “out of date” because of preceding changes, which is a common scenario when working with AI-generated diffs, code from pull requests, or snippets from documentation.
§Core Features
- Markdown-Aware: Directly parses unified diffs from within ````diff ` code blocks.
- Context-Driven: Ignores
@@ ... @@
line numbers, finding patch locations by matching context lines. - Fuzzy Matching: If an exact context match isn’t found,
mpatch
uses a similarity algorithm to find the best fuzzy match. - Safe by Design: Includes a dry-run mode and protection against path traversal attacks.
§Main Workflow
The typical library usage involves two main steps:
- Parsing: Use
parse_diffs
to read a string (e.g., the content of a markdown file) and extract aVec<Patch>
. EachPatch
represents the changes for a single file. - Applying: Iterate through the
Patch
objects and useapply_patch
to apply each one to a target directory on the filesystem.
§Example
Here’s a complete example of how to use the library to patch a file in a temporary directory.
use mpatch::{parse_diffs, apply_patch};
use std::fs;
use tempfile::tempdir;
// 1. Set up a temporary directory and a file to be patched.
let dir = tempdir()?;
let file_path = dir.path().join("src/main.rs");
fs::create_dir_all(file_path.parent().unwrap())?;
fs::write(&file_path, "fn main() {\n println!(\"Hello, world!\");\n}\n")?;
// 2. Define the patch content, as if it came from a markdown file.
let diff_content = r#"
Some introductory text.
```diff
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,3 @@
fn main() {
- println!("Hello, world!");
+ println!("Hello, mpatch!");
}
```
Some concluding text.
"#;
// 3. Parse the diff content to get a list of patches.
let patches = parse_diffs(diff_content)?;
assert_eq!(patches.len(), 1);
let patch = &patches[0];
// 4. Apply the patch.
// For this example, we disable fuzzy matching (fuzz_factor = 0.0)
// and are not doing a dry run.
let success = apply_patch(patch, dir.path(), false, 0.0)?;
// The patch should apply cleanly.
assert!(success);
// 5. Verify the file was changed correctly.
let new_content = fs::read_to_string(&file_path)?;
assert_eq!(new_content, "fn main() {\n println!(\"Hello, mpatch!\");\n}\n");
Structs§
- Hunk
- Represents a single hunk of changes within a patch.
- Patch
- Represents all the changes to be applied to a single file.
Enums§
- Patch
Error - Represents the possible errors that can occur during patch operations.
Functions§
- apply_
patch - Applies a single
Patch
to the specified target directory. - parse_
diffs - Parses a string containing one or more ````diff
blocks into a vector of [
Patch`] objects.