Crate mpatch

Source
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:

  1. Parsing: Use parse_diffs to read a string (e.g., the content of a markdown file) and extract a Vec<Patch>. Each Patch represents the changes for a single file.
  2. Applying: Iterate through the Patch objects and use apply_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§

PatchError
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.