Repository navigation
Releases: Romelium/mpatch
Releases · Romelium/mpatch
Release list
v1.8.1
Fixed
- Input Without Patches: Warn when the input contains no patch, such as a chat reply without a diff or a hunk copied without its
---/+++file headers. Previously the CLI printed nothing unless-vwas given. The exit status is still0. - Unchanged Files: Don't rewrite a file when none of its hunks apply or all of them are already applied. The file's modification time no longer changes, and the log no longer says partial changes were written.
Performance
- Fuzzy Search Without a Match: Skip the character-level diff at a candidate location when the characters it shares with the hunk can't reach the fuzz threshold. Searching a 10,000-line file for a 3-line hunk that isn't there now takes about 0.36 s instead of 1.7 s. Hunks that match land in the same place as before; when no location reaches the threshold, the best score in
FuzzyMatchBelowThresholdcan be lower than before.
v1.8.0
Added
- Patch Target Paths: Resolve targets the way
patch -p1does for directory-tree diffs (diff -Naur pkg.orig pkg, including created and deleted files) and for backup-file diffs (diff -u file.orig file). When a target is missing but exists without its leading directory, patch that file and print a warning. - Already-Applied Hunks: Skip a hunk with a warning when it can only be placed by fuzzy matching and its result is already in the file, so re-applying a patch no longer duplicates its changes. Hunks whose context still matches exactly always apply, so intentional duplicates keep working. Hunks that only delete lines, and hunks whose context still matches exactly after they were applied (such as appends at the end of a list), are not detected.
- Git Executable Mode: Apply Git mode changes between
100644and100755on Unix after the content is written, including created files (new file mode 100755), mode-only patches,-R, and--dry-run. A mode change for a file that neither exists nor is created by the patch fails before anything is written, and--atomicskips modes when the batch is discarded. Library callers can read the changes withparse_git_mode_changes().
Fixed
- Unified Diff Parsing: Recognize
diff -Naurheaders, strip timestamps from---/+++file headers (including timestamps whose tab separator was expanded to spaces), decode Git C-quoted paths, and distinguish file headers from hunk content that begins with---or+++. - Line Endings: Preserve CRLF and mixed line endings when patching a file. Previously the whole file was rewritten with LF endings; now unchanged lines keep their own ending and added lines use the file's most common one.
- Final Newline: Keep a file's final newline unless the patch changes its last line. Previously any hunk that reached the end of the file applied the patch's
\ No newline at end of filestate, so a hunk ending in unchanged context could add or remove the final newline. Now a line kept from the file keeps its own newline and a line added by the patch follows the patch's marker, matching GNUpatch. - CLI Failure Reporting: Exit with an error instead of succeeding silently when the input contains context diffs (
diff -c), Git renames or copies, Git binary patches, or symlink and submodule modes. Warn when a Git executable-mode change is not applied.
v1.7.0
Overview
-
Format-Agnostic LLM Ingestion & Wildcards: Native parsing for Aider search/replace blocks with automatic path inference and multi-line wildcard ellipsis (
...) matching that safely bridges omitted code gaps. -
Transaction-Grade Atomicity: All-or-nothing filesystem guarantees via
-a/--atomicacross CLI, Rust API, and Python bindings, staging edits in-memory and rolling back cleanly on any failure. -
Three-Way Merging & Sub-Line Diagnostics: Built-in 3-way line merges with Diff3 conflict markers (
merge_three_way), sub-line word-level diff visualization (format_inline_diff), and fuzzy file path suggestions (suggest_close_file_paths) for missing targets. -
Unix & Stdin Composability: First-class standard input piping (
git diff | mpatch - ./src), implicit piping, and automated fallback to the current directory (.). -
$O(N + M)$ Scalability & Performance: Single-pass inverted index anchor discovery, Histogram-based sequence comparisons, incumbent-seeded upper-bound diff pruning, zero-allocation window length search, and zero-allocation precomputations eliminating quadratic bottlenecks on files with >10,000 lines. - Anti-Hallucination & Scope Protection: Strict boundary validation including orphan addition guards, definition-aware head/tail context preservation, primary identifier and definition consistency across C-family, JVM, scripting, Lisp, and SQL syntax, and topological anchor relaxation for unnumbered hunks.
Added
Parser & Formats
-
Aider Search/Replace Blocks: Added native support for Aider search/replace blocks (
<<<<<<< SEARCH,=======,>>>>>>> REPLACEalong withORIGINALandUPDATEDsyntax variants) viampatch::parse_aider,mpatch::parse_aider_from_lines, andPatchFormat::Aider. Fences are matched by indentation, so indented fences inside replacement content (such as nested code examples) do not end a block early. -
Automatic Format Detection: Updated
detect_patch,parse_auto, andparse_diffsto recognize and parse Aider search/replace blocks alongside unified diffs, markdown code fences, and conflict markers. -
Deduplicated Patch Merging: Added
mpatch::merge_patchesandPatch::mergeto consolidate multiple patch blocks targeting the same file path into singlePatchobjects with combined hunks in linear$O(N)$ time. -
Heuristic Target File Path Detection: Implemented
extract_file_path_from_line,is_plausible_file_path, andnormalize_candidate_pathto infer target file paths for Aider search/replace blocks from preceding markdown text, headings, code comments (// filepath:), backticks, and search fence headers. A path named in a heading stays in scope across long explanations, illustrative non-patch code blocks, and consecutive patch blocks until a#/##heading without a path or a thematic break (---,***) starts a new section. Conflict marker blocks still default topatch_target. -
File Path Plausibility Rules:
is_plausible_file_pathaccepts quoted or otherwise delimited paths, paths containing spaces or Unicode, route templates ({id}), extensions such as.c++, extensionless build files (BUILD,WORKSPACE,configure,VERSION), and paths up to 4,096 bytes, while rejecting URLs, code syntax, and conversational sentences. -
Wildcard & Ellipsis Matching: Added support for wildcard ellipsis lines (
...,…,// ... existing code ...,<!-- ... -->,# ... rest of function ..., Fortran! ..., Batch:: ..., etc.) across single and multi-segment hunks viais_ellipsis_line. Multi-line code gaps between anchors are accurately preserved while strictly guarding against runaway gaps or syntax false positives. Segments may end on low-entropy anchors such as},end, orpass; a wildcard hunk is rejected only when its leading segment or every segment is low-entropy.
Core API
- Patch Methods: Added
Patch::apply_to_fileandPatch::apply_to_contentas method forms ofapply_patch_to_fileandapply_patch_to_content. - Strict Batch Application: Added
try_apply_patches_to_dir, a non-atomic strict variant ofapply_patches_to_dirthat returnsStrictBatchApplyError::Failedif any patch has a hard error or any hunk fails. - Candidate Inspection: Added
DefaultHunkFinder::find_candidate_locationsto list every candidate location for a hunk in order of preference, andHunk::required_match_spanto compute the minimum window span that can contain all of a hunk's edits. - Comment Line Detection: Added
mpatch::is_comment_lineto classify pure comment, docstring, and banner lines across C-family, scripting, markup, Lisp, SQL, Fortran, and Batch syntaxes, while treating pointer dereferences, preprocessor directives, attributes, pre-decrements, and CSS selectors as code. - Zero-Allocation Window Length Iterator: Added
mpatch::WindowLengthIterandmpatch::window_lengthsimplementingIterator,ExactSizeIterator, andFusedIteratorto generate candidate window lengths radiating outward from nominal length without heap allocations.
Transactional Atomicity
- Core Atomic APIs: Added atomic filesystem application via
apply_patches_to_dir_atomic,try_apply_patches_to_dir_atomic,apply_patch_to_file_atomic,try_apply_patch_to_file_atomic, andPatch::apply_to_file_atomic. File operations are staged in-memory and committed to disk if and only if all hunks across all patches apply cleanly, with automatic rollback of written files upon commit failure. - Batch Outcome Inspection: Added
BatchResult::all_applied_cleanly(),BatchResult::has_failures(), andStrictBatchApplyErrorfor granular programmatic inspection of batch patch outcomes. - CLI Atomic Flag: Introduced the
-a/--atomic(aliased as--all-or-nothing) command-line flag to ensure that changes are only written to disk if every patch and hunk succeeds.
Three-Way Merging & Diagnostics
- 3-Way Line Merge: Added
mpatch::merge_three_waybacked bysimilar::TextMergeto perform 3-way line merges between an ancestor (base), local code (ours), and incoming changes (theirs), with configurable Diff3 conflict marker labels. - Inline Word-Level Diffs: Added
mpatch::format_inline_diffpowered bysimilar'sinlinefeature to format sub-line word additions and deletions with colored terminal highlights. - CLI Near-Miss Diagnostics: Failed hunk diagnostics in the CLI now render sub-line near-miss comparisons with highlighted word differences when encountering
FuzzyMatchBelowThresholderrors. - CLI Path Suggestions: Added
mpatch::suggest_close_file_pathspowered bysimilar::get_close_matchesto suggest close matching file paths in the target directory when a patch specifies a missing target (TargetNotFound).
CLI & Unix Composability
- Standard Input (Stdin) Piping: Added first-class support for piping patches via
-(e.g.,cat patch.diff | mpatch - ./src,git diff | mpatch -R - ./src,curl -s https://example.com/fix.patch | mpatch - ./src), including implicit piping and automatic fallback to the current directory (.) when the target directory argument is omitted.
Python Bindings (mpatch)
- Aider Parsing: Exposed
mpatch.parse_aiderand updateddetect_patchand type stubs (mpatch.pyi) for Aider search/replace blocks. - Atomic Operations: Added the
atomic: bool = Falsekeyword parameter toapply_directory,apply_patches_to_dir,apply_patch_to_file, andPatch.apply_to_file, alongside dedicatedapply_patch_to_file_atomic,apply_patches_to_dir_atomic, andPatch.apply_to_file_atomicfunctions. - Batch Outcome Inspection: Added the
BatchResult.all_applied_cleanlyandBatchResult.has_failuresproperties, and madeBatchResultpath lookups (result[path],path in result) accept either/or\separators. - Deduplicated Patch Merging: Exposed
mpatch.merge_patchesandPatch.merge. - Merge & Inline Functions: Exposed
mpatch.merge_three_wayandmpatch.format_inline_diffwith comprehensive type annotations (mpatch.pyi). - API Expansion: Exposed
mpatch.parse_single_patch,mpatch.suggest_close_file_paths,mpatch.find_hunk_location,Hunk.find_location,Hunk.required_match_span, andmpatch.ensure_path_is_safe. - Path & Wildcard Inspection Utilities: Exposed
mpatch.is_ellipsis_line,mpatch.is_plausible_file_path, andmpatch.extract_file_path_from_line. - Near-Miss Diagnostics: Added
HunkFailure.location_startandHunkFailure.location_lengthto inspect rejected fuzzy near-miss match coordinates.
Tooling & Release Automation
- Release Automation: Added
scripts/release.pyfeaturing interactive pre-flight toolchain diagnostics, automated testing across Rust and Python, lockfile synchronization, atomic git commit/tagging, and optional Crates.io publishing. - Benchmark Suites: Added Criterion benchmarks covering Aider format detection and parsing, interleaved patch merging, a 200-function 3-way merge, 100-line inline diff rendering, Patience diff patch generation, and radiating window length iteration (
benches/mpatch_bench.rs).
Changed
- Diff Engine & Dependencies: Upgraded
similarfrom2.7.0to3.2.0with theinlinefeature enabled. - Error Handling & Styling Dependencies: Upgraded
thiserrorfrom1.0.69to2.0.21andcoloredfrom2.2.0to3.1.1. - Patch Generation Algorithm: Switched
Patch::from_textsand internal search/replace hunk alignment tosimilar::Algorithm::Patiencefor cleaner, semantically aligned hunk boundaries across refactored functions and moved blocks. - Same-File Patch Consolidation:
parse_diffs, and thereforeparse_autoand the CLI on Markdown input, now merges patches that target the same file across separate code blocks into a singlePatchwith combined hunks. Previously each code block produced its ownPatch. - **F...
v1.6.4
Added
- Python Bindings: Introduced official Python bindings via PyO3. The
mpatchpackage is now available on PyPI!- Exposes a rich, object-oriented, and fully typed API (with
.pyistubs) for tool builders and AI agent frameworks. - Provides high-level convenience functions (
patch_content,apply_directory) and granular control over patches and hunks. - Includes pre-compiled wheels for Linux, macOS, and Windows, with support for Python 3.8+ and Python 3.13 free-threaded builds.
- Implements Pythonic paradigms like slicing, unary inversion (
~patch), and native exceptions (ParseError,PathTraversalError).
- Exposes a rich, object-oriented, and fully typed API (with
v1.5.0
Added
- CLI: Added
-c/--clipboardflag to input directly from the system clipboard.
Changed
- Logging: Improved debug and trace logging. Reduced log spam during parsing, elevated key file I/O operations to
debuglevel, and added detailedtracelogs to the smart indentation algorithm (usingescape_debugto visualize tabs vs. spaces). Also added visibility intoparse_autoformat detection and fallback behavior. - Diagnostics: Significantly enhanced the anonymization of debug reports (
-vvvv). The report generator now actively redacts the input file path, target directory, current working directory, and user home directory from the entire report, including the full trace log, file contents, and error messages. - Diagnostics: The discrepancy check failure output in the debug report (
-vvvv) now includes a unified diff between the original input patch and the regenerated patch to make differences easier to spot. The full patch contents are now placed inside a collapsible<details>block to reduce visual clutter. - Diagnostics: Improved the discrepancy check to normalize patches before comparison. It now ignores context lines, hunk headers, +/- interleaving, and self-replacements, significantly reducing noise and eliminating false positives caused by structural differences.
Security
- Path Validation: Fixed a vulnerability in
ensure_path_is_safewhere a dangling symlink could bypass the path traversal check and allow arbitrary file creation outside the target directory. The validation now correctly detects dangling symlinks usingsymlink_metadata.
Fixed
- CLI: Fixed a critical bug where
--dry-runwould incorrectly create parent directories on the filesystem. The path safety check now performs symlink resolution without requiring intermediate directory creation. - CLI/API: Fixed an issue where the preview diff generated during a dry run (
PatchResult::diff) contained hardcoded"a"and"b"file headers instead of the actual target file paths. - Patch Application: Fixed a bug where the trailing newline status could be handled incorrectly if a patch contained out-of-order hunks that modified the end of the file before modifying earlier lines.
v1.4.4
Security
- Path Validation: Fixed a directory traversal vulnerability in
ensure_path_is_safewhere parent directories of a patch target were created on the filesystem before the path was validated. The function now performs a lexical path validation to ensure the path does not escape the base directory before interacting with the filesystem, preventing arbitrary directory creation.
Fixed
- Patch Application: Fixed a bug in the smart indentation adjustment where empty lines containing trailing whitespace were incorrectly used to calculate indentation drift. The logic now strictly requires both the patch line and the target line to contain non-whitespace characters before updating the indentation context.
v1.4.3
Fixed
- Patch Application: Fixed a critical bug in the fuzzy matching reconstruction logic where differing indentation levels between the patch and the target file could cause the internal alignment to scramble (often anchoring incorrectly on empty lines). The alignment is now based on trimmed lines, ensuring semantic correctness while still dynamically adjusting indentation during application.
v1.4.2
Fixed
- Patch Application: Fixed smart indentation adjustment to correctly translate multiple levels of indentation between spaces and tabs, making it robust against nested patches (e.g., inside markdown lists) by defaulting to 4 spaces per tab if the ratio is skewed. Also fixed an issue where empty lines (or lines with only whitespace) would result in trailing whitespace being added.
- Patch Application: Fixed a bug where the auto-indentation logic would lose track of the target file's indentation style (e.g., tabs vs. spaces) when encountering unindented lines within the patch context.
- Patch Application: Fixed a bug in the fallback logic for adjusting indentation of lines that are outdented relative to the hunk's context. The logic now correctly strips the indentation difference from the end of the line's actual indentation.
- CLI: Fixed a bug in the debug report's discrepancy check (
-vvvv) where patches that created or deleted files were skipped. The check now correctly treats non-existent files as empty strings, allowing verification to proceed. - CLI: Fixed a false positive in the debug report's discrepancy check (
-vvvv) where patches with out-of-order hunks were incorrectly flagged as failures. The check now correctly verifies hunks regardless of their order.
v1.4.1
Fixed
- Parser: Fixed a bug where empty lines between file diffs (common in LLM outputs) were incorrectly absorbed as trailing context lines, breaking file creation and deletion detection.
- API: Made
Patch::is_creation()andPatch::is_deletion()more robust by checking hunk header line numbers (e.g.,@@ -0,0 ...). - Fuzzy Matching: Fixed a bug in the search optimization heuristic where indentation differences between the patch and the target file would cause the fuzzy search to fail or look in the wrong location. The anchor line search now correctly ignores leading whitespace.
v1.4.0
Performance
- Patch Creation: Optimized
Patch::from_textsto construct hunks directly from diff operations, avoiding the overhead of generating and re-parsing a unified diff string. - Formatting: Optimized
Hunk::fmtto calculate line counts in a single pass.
Fixed
- Patch Application: Fixed a bug where a trailing newline was incorrectly added to files lacking one, even if the patch only modified lines at the beginning or middle of the file. The original newline status is now preserved unless the patch explicitly modifies the end of the file.
- Patch Application: Fixed a bug where files resulting in exactly one newline (e.g., replacing all content with a single empty line) were incorrectly truncated to 0 bytes.
- Parser: Fixed handling of the
\ No newline at end of filemarker. It now correctly verifies that the marker immediately follows a context or addition line. - Parser: Fixed parsing of empty hunks (e.g.,
@@ -0,0 +0,0 @@) which were previously ignored. - Parser: Fixed a bug where a context line in a diff that looks like a closing fence (e.g.,
```) would incorrectly terminate the markdown code block. The parser now enforces that a closing fence must not be more indented than the opening fence. - Parser: Improved Conflict Marker detection to prevent false positives from Markdown H1 headers (lines of
====). Detection now strictly requires a start marker (<<<<) followed by a middle (====) or end (>>>>) marker. - CLI: Fixed formatting in the debug report (
-vvvv) where final summary logs were appended outside the markdown code block, breaking the report structure.
Added
- CLI: Added
-R/--reverseflag to reverse patches before applying them (useful for undoing changes). - API: Added
mpatch::invert_patcheshelper function to programmatically invert a list of patches. - API: Added
HunkApplier::set_original_newline_statusto manually control the expected newline behavior when using the line-based API. - API: Derived
PartialEqforApplyOptions.
Changed
- Patch Application: Files are now automatically deleted if the patch application results in empty content.
- Internal: Refactored debug report finalization to be more robust and prevent redundant file locking.