diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9503f0..0f2361f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,6 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.80.0 + toolchain: 1.89.0 override: true - run: cargo check diff --git a/Cargo.toml b/Cargo.toml index 22791fb..ae9b7c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/bmwill/diffy" readme = "README.md" keywords = ["diff", "patch", "merge"] categories = ["text-processing"] -rust-version = "1.80.0" -edition = "2021" +rust-version = "1.89.0" +edition = "2024" [dependencies] nu-ansi-term = "0.50" @@ -19,4 +19,4 @@ thiserror = "2.0.12" [dev-dependencies] insta = { version = "1.43.1", features = ["glob"] } -rstest = "0.25.0" +rstest = "0.26.1" diff --git a/src/apply.rs b/src/apply.rs index b45ac84..14c7330 100644 --- a/src/apply.rs +++ b/src/apply.rs @@ -168,11 +168,7 @@ impl FuzzyComparable for [u8] { s1.similarity(s2, config) } else { // Fall back to exact byte comparison - if self == other { - 1.0 - } else { - 0.0 - } + if self == other { 1.0 } else { 0.0 } } } } @@ -712,7 +708,7 @@ where mod test { use std::path::PathBuf; - use crate::{apply, Diff}; + use crate::{Diff, apply}; fn load_files(name: &str) -> (String, String) { let base_folder = PathBuf::from(env!("CARGO_MANIFEST_DIR")) diff --git a/src/diff/mod.rs b/src/diff/mod.rs index 863fa45..cc4e412 100644 --- a/src/diff/mod.rs +++ b/src/diff/mod.rs @@ -1,8 +1,8 @@ use crate::{ + LineEnd, patch::{Diff, Hunk, HunkRange, Line}, range::{DiffRange, SliceLike}, utils::{Classifier, Text}, - LineEnd, }; use std::{borrow::Cow, cmp, ops}; diff --git a/src/diff/tests.rs b/src/diff/tests.rs index 329fb76..4af2354 100644 --- a/src/diff/tests.rs +++ b/src/diff/tests.rs @@ -1,10 +1,10 @@ use super::*; use crate::{ + PatchFormatter, apply::apply, diff::{DiffLine, DiffRange}, patch::Diff, range::Range, - PatchFormatter, }; // Helper macros are based off of the ones used in [dissimilar](https://docs.rs/dissimilar) @@ -769,7 +769,7 @@ Second: let elapsed = now.elapsed(); println!("{:?}", elapsed); - assert!(elapsed < std::time::Duration::from_micros(400)); + assert!(elapsed < std::time::Duration::from_micros(600)); assert_eq!(result, expected); } diff --git a/src/lib.rs b/src/lib.rs index cdc149d..6b7e99d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,14 +228,14 @@ mod range; mod utils; pub use apply::{ - apply, apply_bytes, apply_bytes_with_config, apply_with_config, ApplyConfig, ApplyError, - FuzzyConfig, LineEndHandling, + ApplyConfig, ApplyError, FuzzyConfig, LineEndHandling, apply, apply_bytes, + apply_bytes_with_config, apply_with_config, }; -pub use diff::{create_patch, create_patch_bytes, DiffOptions}; +pub use diff::{DiffOptions, create_patch, create_patch_bytes}; pub use line_end::*; -pub use merge::{merge, merge_bytes, ConflictStyle, MergeOptions}; +pub use merge::{ConflictStyle, MergeOptions, merge, merge_bytes}; pub use patch::{ - patch_from_bytes, patch_from_bytes_with_config, patch_from_str, patch_from_str_with_config, Diff, Hunk, HunkRange, HunkRangeStrategy, Line, ParsePatchError, ParserConfig, Patch, - PatchFormatter, + PatchFormatter, patch_from_bytes, patch_from_bytes_with_config, patch_from_str, + patch_from_str_with_config, }; diff --git a/src/merge/mod.rs b/src/merge/mod.rs index cf9dee5..aac72b6 100644 --- a/src/merge/mod.rs +++ b/src/merge/mod.rs @@ -1,8 +1,8 @@ use crate::{ + LineEnd, diff::DiffOptions, range::{DiffRange, Range, SliceLike}, utils::Classifier, - LineEnd, }; use std::{cmp, fmt}; @@ -365,10 +365,10 @@ fn merge_solutions<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>( solution.push(merge_range); - if ours.map_or(true, |range| range.is_empty()) { + if ours.is_none_or(|range| range.is_empty()) { ours = our_solution.next(); } - if theirs.map_or(true, |range| range.is_empty()) { + if theirs.is_none_or(|range| range.is_empty()) { theirs = their_solution.next(); } } diff --git a/src/patch/mod.rs b/src/patch/mod.rs index 031a449..6d2e9b2 100644 --- a/src/patch/mod.rs +++ b/src/patch/mod.rs @@ -10,7 +10,7 @@ use std::{ ops, }; -use crate::{utils::Text, LineEnd}; +use crate::{LineEnd, utils::Text}; const NO_NEWLINE_AT_EOF: &str = "\\ No newline at end of file"; diff --git a/src/patch/parse.rs b/src/patch/parse.rs index 6f1d170..32577da 100644 --- a/src/patch/parse.rs +++ b/src/patch/parse.rs @@ -1,10 +1,10 @@ //! Parse a Patch -use super::{Hunk, HunkRange, Line, ESCAPED_CHARS_BYTES, NO_NEWLINE_AT_EOF}; +use super::{ESCAPED_CHARS_BYTES, Hunk, HunkRange, Line, NO_NEWLINE_AT_EOF}; use crate::{ + LineEnd, patch::Diff, utils::{LineIter, Text}, - LineEnd, }; use std::{borrow::Cow, fmt}; @@ -145,6 +145,14 @@ pub fn parse_multiple_with_config(input: &str, config: ParserConfig) -> Result break, + // Allow NoHunks error when we have valid headers (pure renames/deletes/adds) + (Ok(header), Err(ParsePatchError::NoHunks)) + if header.0.is_some() || header.1.is_some() => + { + let original = header.0.map(|(line, _end)| convert_cow_to_str(line)); + let modified = header.1.map(|(line, _end)| convert_cow_to_str(line)); + patches.push(Diff::new(original, modified, vec![])) + } (Ok(_), Err(e)) | (Err(e), _) => { return Err(e); } @@ -183,6 +191,14 @@ pub fn parse_bytes_multiple_with_config( patches.push(Diff::new(original, modified, hunks)) } (Ok((None, None)), Err(_)) | (Err(_), Err(_)) => break, + // Allow NoHunks error when we have valid headers (pure renames/deletes/adds) + (Ok(header), Err(ParsePatchError::NoHunks)) + if header.0.is_some() || header.1.is_some() => + { + let original = header.0.map(|(line, _end)| line); + let modified = header.1.map(|(line, _end)| line); + patches.push(Diff::new(original, modified, vec![])) + } (Ok(_), Err(e)) | (Err(e), Ok(_)) => { return Err(e); } @@ -217,49 +233,125 @@ fn patch_header<'a, T: Text + ToOwned + ?Sized>( Option<(Cow<'a, [u8]>, Option)>, Option<(Cow<'a, [u8]>, Option)>, )> { - skip_header_preamble(parser)?; + let (git_original, git_modified, saw_git_header) = header_preamble(parser)?; let mut filename1 = None; let mut filename2 = None; + let mut saw_traditional_header1 = false; + let mut saw_traditional_header2 = false; while let Some((line, _end)) = parser.peek() { if line.starts_with("--- ") { - if filename1.is_some() { + if saw_traditional_header1 { return Err(ParsePatchError::HeaderMultipleLines( HeaderLineKind::Removing, )); } - filename1 = Some(parse_filename("--- ", parser.next()?)?); + saw_traditional_header1 = true; + filename1 = parse_filename("--- ", parser.next()?, saw_git_header)?; } else if line.starts_with("+++ ") { - if filename2.is_some() { + if saw_traditional_header2 { return Err(ParsePatchError::HeaderMultipleLines(HeaderLineKind::Adding)); } - filename2 = Some(parse_filename("+++ ", parser.next()?)?); + saw_traditional_header2 = true; + filename2 = parse_filename("+++ ", parser.next()?, saw_git_header)?; } else { break; } } - Ok((filename1, filename2)) + // Traditional --- +++ headers take precedence over git metadata + // If we saw a traditional header (even if it parsed to None for /dev/null), use it + // Otherwise fall back to git metadata + let original = if saw_traditional_header1 { + filename1 + } else { + git_original + }; + let modified = if saw_traditional_header2 { + filename2 + } else { + git_modified + }; + + Ok((original, modified)) } -// Skip to the first filename header ("--- " or "+++ ") or hunk line, -// skipping any preamble lines like "diff --git", git metadata, etc. -fn skip_header_preamble(parser: &mut Parser<'_, T>) -> Result<()> { - while let Some((line, _end)) = parser.peek() { +// Parse the patch header preamble, extracting filenames from git metadata. +// Skips preamble lines like "diff --git", git metadata, etc., until reaching +// the first filename header ("--- " or "+++ ") or hunk line. +// Returns extracted filenames from git metadata (for pure renames/deletes/adds) +// and a flag indicating whether we saw a git header (for prefix stripping). +#[allow(clippy::type_complexity)] +fn header_preamble<'a, T: Text + ToOwned + ?Sized>( + parser: &mut Parser<'a, T>, +) -> Result<( + Option<(Cow<'a, [u8]>, Option)>, + Option<(Cow<'a, [u8]>, Option)>, + bool, // saw_git_header +)> { + let mut git_original = None; + let mut git_modified = None; + let mut rename_from = None; + let mut rename_to = None; + let mut seen_diff_git = false; + + while let Some((line, end)) = parser.peek() { if line.starts_with("--- ") | line.starts_with("+++ ") | line.starts_with("@@ ") { break; } + + // Parse git diff header: diff --git a/... b/... + if line.starts_with("diff --git ") { + // If we've already seen a diff --git line and have extracted data, + // this is the start of the next patch, so stop here + if seen_diff_git && (git_original.is_some() || rename_from.is_some()) { + break; + } + seen_diff_git = true; + + if let Some(rest) = line.strip_prefix("diff --git ") { + // Parse "a/file1 b/file2" (with prefixes) or "file1 file2" (without prefixes) + // Try to split on " b/" first to detect standard format + if let Some((file1, file2)) = rest.split_at_exclusive(" b/") { + // Standard format with b/ prefix + git_original = parse_git_filename(file1, true).map(|f| (f, *end)); + git_modified = parse_git_filename(file2, true).map(|f| (f, *end)); + } else if let Some((file1, file2)) = rest.split_at_exclusive(" ") { + // --no-prefix format + git_original = parse_git_filename(file1, false).map(|f| (f, *end)); + git_modified = parse_git_filename(file2, false).map(|f| (f, *end)); + } + // If neither split works, skip this line (malformed diff --git line) + } + } + // Parse rename from/to + else if line.starts_with("rename from ") + && let Some(filename) = line.strip_prefix("rename from ") + { + rename_from = Some((Cow::Borrowed(filename.as_bytes()), *end)); + } else if line.starts_with("rename to ") + && let Some(filename) = line.strip_prefix("rename to ") + { + rename_to = Some((Cow::Borrowed(filename.as_bytes()), *end)); + } + parser.next()?; } - Ok(()) + // Prefer rename from/to over git diff header + let original = rename_from.or(git_original); + let modified = rename_to.or(git_modified); + + Ok((original, modified, seen_diff_git)) } +#[allow(clippy::type_complexity)] fn parse_filename<'a, T: Text + ToOwned + ?Sized>( prefix: &str, l: (&'a T, Option), -) -> Result<(Cow<'a, [u8]>, Option)> { + saw_git_header: bool, +) -> Result, Option)>> { let line = l.0.strip_prefix(prefix) .ok_or(ParsePatchError::UnableToParseFilename)?; @@ -274,13 +366,33 @@ fn parse_filename<'a, T: Text + ToOwned + ?Sized>( line }; - let filename = if let Some(quoted) = is_quoted(filename) { + // Check for /dev/null before parsing + if filename.as_bytes() == b"/dev/null" { + return Ok(None); + } + + let mut parsed_filename = if let Some(quoted) = is_quoted(filename) { escaped_filename(quoted)? } else { unescaped_filename(filename)? }; - Ok((filename, l.1)) + // Strip a/ or b/ prefix only if we saw a git header (git format uses these prefixes) + if saw_git_header && let Cow::Borrowed(bytes) = parsed_filename { + if let Some(rest) = std::str::from_utf8(bytes) + .ok() + .and_then(|s| s.strip_prefix("a/")) + { + parsed_filename = Cow::Borrowed(rest.as_bytes()); + } else if let Some(rest) = std::str::from_utf8(bytes) + .ok() + .and_then(|s| s.strip_prefix("b/")) + { + parsed_filename = Cow::Borrowed(rest.as_bytes()); + } + } + + Ok(Some((parsed_filename, l.1))) } fn is_quoted(s: &T) -> Option<&T> { @@ -327,6 +439,31 @@ fn escaped_filename(escaped: &T) -> Result(filename: &T, has_prefix: bool) -> Option> { + // Check for /dev/null (file doesn't exist) + if filename.as_bytes() == b"/dev/null" { + return None; + } + + // If we detected prefixes (found " b/" in the line), strip a/ or b/ + if has_prefix { + if let Some(stripped) = filename.strip_prefix("a/") { + return Some(Cow::Borrowed(stripped.as_bytes())); + } else if let Some(stripped) = filename.strip_prefix("b/") { + return Some(Cow::Borrowed(stripped.as_bytes())); + } + } + + // No prefix or couldn't strip, use as-is + Some(Cow::Borrowed(filename.as_bytes())) +} + fn verify_hunks_in_order(hunks: &[Hunk<'_, T>]) -> bool { for hunk in hunks.windows(2) { if hunk[0].old_range.end() > hunk[1].old_range.start() @@ -551,10 +688,10 @@ fn hunk_lines<'a, T: Text + ?Sized + ToOwned>( #[cfg(test)] mod tests { - use crate::patch::parse::{parse_multiple_with_config, HunkRangeStrategy, ParserConfig}; use crate::patch::Line; + use crate::patch::parse::{HunkRangeStrategy, ParserConfig, parse_multiple_with_config}; - use super::{parse, parse_bytes}; + use super::{parse, parse_bytes, parse_multiple}; #[test] fn test_escaped_filenames() { @@ -760,4 +897,213 @@ mod tests { _ => panic!("Expected insert line with 'From: new@example.com'"), } } + + #[test] + fn test_pure_renames() { + // Test parsing patches with pure renames (no hunks, only git metadata) + let patch = r#"diff --git a/old_file.txt b/new_file.txt +similarity index 100% +rename from old_file.txt +rename to new_file.txt +diff --git a/another_old.txt b/another_new.txt +similarity index 100% +rename from another_old.txt +rename to another_new.txt +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 2, "Should parse two rename patches"); + + // First rename + assert_eq!(result[0].original(), Some("old_file.txt")); + assert_eq!(result[0].modified(), Some("new_file.txt")); + assert_eq!( + result[0].hunks().len(), + 0, + "Pure rename should have no hunks" + ); + + // Second rename + assert_eq!(result[1].original(), Some("another_old.txt")); + assert_eq!(result[1].modified(), Some("another_new.txt")); + assert_eq!( + result[1].hunks().len(), + 0, + "Pure rename should have no hunks" + ); + } + + #[test] + fn test_deleted_file() { + // Test parsing patches with deleted files + let patch = r#"diff --git a/deleted_file.txt b/deleted_file.txt +deleted file mode 100644 +index e69de29bb2d1d..0000000000000 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1, "Should parse one delete patch"); + + assert_eq!(result[0].original(), Some("deleted_file.txt")); + assert_eq!(result[0].modified(), Some("deleted_file.txt")); + assert_eq!( + result[0].hunks().len(), + 0, + "Deleted file should have no hunks" + ); + } + + #[test] + fn test_git_diff_without_rename_metadata() { + // Test that we can extract filenames from diff --git line even without rename from/to + let patch = r#"diff --git a/file1.txt b/file2.txt +similarity index 100% +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + assert_eq!(result[0].original(), Some("file1.txt")); + assert_eq!(result[0].modified(), Some("file2.txt")); + assert_eq!(result[0].hunks().len(), 0); + } + + #[test] + fn test_git_diff_without_prefix() { + // Test git diff --no-prefix format (no a/ and b/ prefixes) + let patch = r#"diff --git old_file.txt new_file.txt +similarity index 100% +rename from old_file.txt +rename to new_file.txt +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1, "Should parse one rename patch"); + + // Should prefer rename from/to over diff --git when both present + assert_eq!(result[0].original(), Some("old_file.txt")); + assert_eq!(result[0].modified(), Some("new_file.txt")); + assert_eq!(result[0].hunks().len(), 0); + } + + #[test] + fn test_git_diff_no_prefix_without_rename_metadata() { + // Test git diff --no-prefix format without rename from/to metadata + let patch = r#"diff --git deleted_file.txt deleted_file.txt +deleted file mode 100644 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + assert_eq!(result[0].original(), Some("deleted_file.txt")); + assert_eq!(result[0].modified(), Some("deleted_file.txt")); + assert_eq!(result[0].hunks().len(), 0); + } + + #[test] + fn test_git_diff_no_prefix_with_a_in_filename() { + // Edge case: --no-prefix format with file literally named "a/something.txt" + // Should NOT strip the a/ since we didn't find b/ prefix + let patch = r#"diff --git a/file.txt a/file.txt +deleted file mode 100644 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + // In --no-prefix mode, filename is literally "a/file.txt" + assert_eq!(result[0].original(), Some("a/file.txt")); + assert_eq!(result[0].modified(), Some("a/file.txt")); + assert_eq!(result[0].hunks().len(), 0); + } + + #[test] + fn test_git_diff_new_file_with_dev_null() { + // Test creating a new file - git header doesn't actually use /dev/null in the diff --git line + // but the --- header does + let patch = r#"diff --git a/new_file.txt b/new_file.txt +new file mode 100644 +--- /dev/null ++++ b/new_file.txt +@@ -0,0 +1,3 @@ ++line 1 ++line 2 ++line 3 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + // The --- /dev/null should override the git header + assert_eq!(result[0].original(), None); + assert_eq!(result[0].modified(), Some("new_file.txt")); + assert_eq!(result[0].hunks().len(), 1); + } + + #[test] + fn test_git_diff_deleted_file_with_dev_null() { + // Test deleting a file - git header doesn't actually use /dev/null in the diff --git line + // but the +++ header does + let patch = r#"diff --git a/deleted_file.txt b/deleted_file.txt +deleted file mode 100644 +--- a/deleted_file.txt ++++ /dev/null +@@ -1,3 +0,0 @@ +-line 1 +-line 2 +-line 3 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + assert_eq!(result[0].original(), Some("deleted_file.txt")); + // The +++ /dev/null should override the git header + assert_eq!(result[0].modified(), None); + assert_eq!(result[0].hunks().len(), 1); + } + + #[test] + fn test_git_diff_dev_null_in_git_header() { + // Test /dev/null in the git diff header itself + let patch = r#"diff --git /dev/null b/new_file.txt +new file mode 100644 +--- /dev/null ++++ b/new_file.txt +@@ -0,0 +1,2 @@ ++new content ++here +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + // Both from git header and --- header should recognize /dev/null + assert_eq!(result[0].original(), None); + assert_eq!(result[0].modified(), Some("new_file.txt")); + assert_eq!(result[0].hunks().len(), 1); + } + + #[test] + fn test_traditional_unified_diff_no_git_header() { + // Test traditional unified diff format WITHOUT git header + // These should NOT have a/ b/ prefixes stripped + let patch = r#"--- a/old_file.txt ++++ a/new_file.txt +@@ -1,3 +1,3 @@ + line 1 +-old line ++new line + line 3 +"#; + + let result = parse_multiple(patch).unwrap(); + assert_eq!(result.len(), 1); + + // Without diff --git, the a/ prefix should be preserved (it's part of the actual filename) + assert_eq!(result[0].original(), Some("a/old_file.txt")); + assert_eq!(result[0].modified(), Some("a/new_file.txt")); + assert_eq!(result[0].hunks().len(), 1); + } } diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Fix-support-for-newer-GCC.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Fix-support-for-newer-GCC.patch.snap index 575ec6e..be9d89c 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Fix-support-for-newer-GCC.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Fix-support-for-newer-GCC.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/CMakeModules/CompilerChecks.cmake", + "CMakeModules/CompilerChecks.cmake", ), ), modified: Some( Filename( - "b/CMakeModules/CompilerChecks.cmake", + "CMakeModules/CompilerChecks.cmake", ), ), hunks: [ @@ -61,12 +61,12 @@ Ok( Patch { original: Some( Filename( - "a/Tests/GTest/src/gtest-death-test.cc", + "Tests/GTest/src/gtest-death-test.cc", ), ), modified: Some( Filename( - "b/Tests/GTest/src/gtest-death-test.cc", + "Tests/GTest/src/gtest-death-test.cc", ), ), hunks: [ @@ -138,12 +138,12 @@ Ok( Patch { original: Some( Filename( - "a/include/Hammer/Math/Utils.hh", + "include/Hammer/Math/Utils.hh", ), ), modified: Some( Filename( - "b/include/Hammer/Math/Utils.hh", + "include/Hammer/Math/Utils.hh", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Force-CMake-to-use-the-correct-Python.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Force-CMake-to-use-the-correct-Python.patch.snap index db008ab..9a9be95 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Force-CMake-to-use-the-correct-Python.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-Force-CMake-to-use-the-correct-Python.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/setup.py", + "setup.py", ), ), modified: Some( Filename( - "b/setup.py", + "setup.py", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-use-C-style-for-including-inttypes-in-bin-mqtt5_cana.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-use-C-style-for-including-inttypes-in-bin-mqtt5_cana.patch.snap index 79e43fb..70321ac 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-use-C-style-for-including-inttypes-in-bin-mqtt5_cana.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0001-use-C-style-for-including-inttypes-in-bin-mqtt5_cana.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/bin/mqtt5_canary/main.cpp", + "bin/mqtt5_canary/main.cpp", ), ), modified: Some( Filename( - "b/bin/mqtt5_canary/main.cpp", + "bin/mqtt5_canary/main.cpp", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Allow-to-specify-git-sha-through-environment-variabl.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Allow-to-specify-git-sha-through-environment-variabl.patch.snap index 16348a3..32c4315 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Allow-to-specify-git-sha-through-environment-variabl.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Allow-to-specify-git-sha-through-environment-variabl.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/setup.py", + "setup.py", ), ), modified: Some( Filename( - "b/setup.py", + "setup.py", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Fix-lib-suffix.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Fix-lib-suffix.patch.snap index 001093d..1ddfc3f 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Fix-lib-suffix.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0002-Fix-lib-suffix.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/cmake/modules/DefineInstallationPaths.cmake", + "cmake/modules/DefineInstallationPaths.cmake", ), ), modified: Some( Filename( - "b/cmake/modules/DefineInstallationPaths.cmake", + "cmake/modules/DefineInstallationPaths.cmake", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0003-Fix-LDFLAGS-for-finding-ldap.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0003-Fix-LDFLAGS-for-finding-ldap.patch.snap index 62ae28c..ac7300e 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0003-Fix-LDFLAGS-for-finding-ldap.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@0003-Fix-LDFLAGS-for-finding-ldap.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/configure.ac", + "configure.ac", ), ), modified: Some( Filename( - "b/configure.ac", + "configure.ac", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@40.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@40.patch.snap index 5ce1609..96d9f3f 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@40.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@40.patch.snap @@ -6,14 +6,10 @@ input_file: src/patch/test-data/40.patch Ok( [ Patch { - original: Some( - Filename( - "/dev/null", - ), - ), + original: None, modified: Some( Filename( - "b/cvmfs/scripts/__init__.py", + "cvmfs/scripts/__init__.py", ), ), hunks: [ @@ -36,14 +32,10 @@ Ok( ], }, Patch { - original: Some( - Filename( - "/dev/null", - ), - ), + original: None, modified: Some( Filename( - "b/cvmfs/scripts/big_catalogs.py", + "cvmfs/scripts/big_catalogs.py", ), ), hunks: [ @@ -183,14 +175,10 @@ Ok( ], }, Patch { - original: Some( - Filename( - "/dev/null", - ), - ), + original: None, modified: Some( Filename( - "b/cvmfs/scripts/catdirusage.py", + "cvmfs/scripts/catdirusage.py", ), ), hunks: [ @@ -344,12 +332,12 @@ Ok( Patch { original: Some( Filename( - "a/utils/cvmfs_search", + "utils/cvmfs_search", ), ), modified: Some( Filename( - "b/cvmfs/scripts/cvmfs_search.py", + "cvmfs/scripts/cvmfs_search.py", ), ), hunks: [ @@ -431,14 +419,10 @@ Ok( ], }, Patch { - original: Some( - Filename( - "/dev/null", - ), - ), + original: None, modified: Some( Filename( - "b/pyproject.toml", + "pyproject.toml", ), ), hunks: [ @@ -610,14 +594,10 @@ Ok( Patch { original: Some( Filename( - "a/setup.py", - ), - ), - modified: Some( - Filename( - "/dev/null", + "setup.py", ), ), + modified: None, hunks: [ Hunk { old_range: HunkRange { @@ -769,14 +749,10 @@ Ok( Patch { original: Some( Filename( - "a/utils/big_catalogs", - ), - ), - modified: Some( - Filename( - "/dev/null", + "utils/big_catalogs", ), ), + modified: None, hunks: [ Hunk { old_range: HunkRange { @@ -895,14 +871,10 @@ Ok( Patch { original: Some( Filename( - "a/utils/catdirusage", - ), - ), - modified: Some( - Filename( - "/dev/null", + "utils/catdirusage", ), ), + modified: None, hunks: [ Hunk { old_range: HunkRange { @@ -1033,12 +1005,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/__init__.py", + "cvmfs/__init__.py", ), ), modified: Some( Filename( - "b/cvmfs/__init__.py", + "cvmfs/__init__.py", ), ), hunks: [ @@ -1086,12 +1058,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/big_catalogs.py", + "cvmfs/scripts/big_catalogs.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/big_catalogs.py", + "cvmfs/scripts/big_catalogs.py", ), ), hunks: [ @@ -1281,12 +1253,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/catdirusage.py", + "cvmfs/scripts/catdirusage.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/catdirusage.py", + "cvmfs/scripts/catdirusage.py", ), ), hunks: [ @@ -1428,12 +1400,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/cvmfs_search.py", + "cvmfs/scripts/cvmfs_search.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/cvmfs_search.py", + "cvmfs/scripts/cvmfs_search.py", ), ), hunks: [ @@ -1481,12 +1453,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/__init__.py", + "cvmfs/scripts/__init__.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/__init__.py", + "cvmfs/scripts/__init__.py", ), ), hunks: [ @@ -1514,12 +1486,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/big_catalogs.py", + "cvmfs/scripts/big_catalogs.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/big_catalogs.py", + "cvmfs/scripts/big_catalogs.py", ), ), hunks: [ @@ -1558,12 +1530,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/catdirusage.py", + "cvmfs/scripts/catdirusage.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/catdirusage.py", + "cvmfs/scripts/catdirusage.py", ), ), hunks: [ @@ -1602,12 +1574,12 @@ Ok( Patch { original: Some( Filename( - "a/cvmfs/scripts/cvmfs_search.py", + "cvmfs/scripts/cvmfs_search.py", ), ), modified: Some( Filename( - "b/cvmfs/scripts/cvmfs_search.py", + "cvmfs/scripts/cvmfs_search.py", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@Makefile.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@Makefile.patch.snap index 6922bbc..21f70f2 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@Makefile.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@Makefile.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/src/Makefile", + "src/Makefile", ), ), modified: Some( Filename( - "b/src/Makefile", + "src/Makefile", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@define_byteswap.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@define_byteswap.patch.snap index 245557e..a344ac5 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@define_byteswap.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@define_byteswap.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/image/decode/segdec.c", + "image/decode/segdec.c", ), ), modified: Some( Filename( - "b/image/decode/segdec.c", + "image/decode/segdec.c", ), ), hunks: [ @@ -64,12 +64,12 @@ Ok( Patch { original: Some( Filename( - "a/jxrgluelib/JXRGlueJxr.c", + "jxrgluelib/JXRGlueJxr.c", ), ), modified: Some( Filename( - "b/jxrgluelib/JXRGlueJxr.c", + "jxrgluelib/JXRGlueJxr.c", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix-model-path-in-config.prc-template.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix-model-path-in-config.prc-template.patch.snap index 112e2a2..e014b7c 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix-model-path-in-config.prc-template.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix-model-path-in-config.prc-template.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/makepanda/config.in", + "makepanda/config.in", ), ), modified: Some( Filename( - "b/makepanda/config.in", + "makepanda/config.in", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix_gitignore_ms.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix_gitignore_ms.patch.snap index f49a5ad..a4f062b 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix_gitignore_ms.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@fix_gitignore_ms.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/.gitignore", + ".gitignore", ), ), modified: Some( Filename( - "b/.gitignore", + ".gitignore", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@luaconf.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@luaconf.patch.snap index 11625c8..9102da6 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@luaconf.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@luaconf.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/src/luaconf.h", + "src/luaconf.h", ), ), modified: Some( Filename( - "b/src/luaconf.h", + "src/luaconf.h", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@nested.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@nested.patch.snap index b2855da..0975ea8 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@nested.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@nested.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/src/lib/hello.txt", + "src/lib/hello.txt", ), ), modified: Some( Filename( - "b/src/lib/hello.txt", + "src/lib/hello.txt", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@pure-renames.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@pure-renames.patch.snap new file mode 100644 index 0000000..8b7310e --- /dev/null +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@pure-renames.patch.snap @@ -0,0 +1,991 @@ +--- +source: src/patch/parse.rs +expression: patches +input_file: src/patch/test-data/pure-renames.patch +--- +Ok( + [ + Patch { + original: Some( + Filename( + "examples/benchmark_onnx.py", + ), + ), + modified: Some( + Filename( + "examples/benchmark_onnx.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 6, + }, + new_range: HunkRange { + start: 1, + len: 6, + }, + function_context: None, + lines: [ + Context( + "import sys, time\n", + ), + Context( + "from tinygrad import TinyJit, GlobalCounters, fetch, getenv\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from extra.onnx_helpers import get_example_inputs, validate\n", + ), + Context( + "\n", + ), + Context( + "def load_onnx_model(onnx_file):\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "examples/compile_tensorflow.py", + ), + ), + modified: Some( + Filename( + "examples/compile_tensorflow.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 8, + len: 7, + }, + new_range: HunkRange { + start: 8, + len: 7, + }, + function_context: None, + lines: [ + Context( + "import subprocess\n", + ), + Context( + "import tensorflow as tf\n", + ), + Context( + "import tf2onnx\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from tinygrad.tensor import Tensor\n", + ), + Context( + "from tinygrad.helpers import to_mv\n", + ), + Context( + "from extra.export_model import export_model_clang, compile_net, jit_model\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "examples/openpilot/compile3.py", + ), + ), + modified: Some( + Filename( + "examples/openpilot/compile3.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 10, + len: 7, + }, + new_range: HunkRange { + start: 10, + len: 7, + }, + function_context: None, + lines: [ + Context( + "from tinygrad.engine.realize import CompiledRunner\n", + ), + Context( + "\n", + ), + Context( + "import onnx\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "\n", + ), + Context( + "OPENPILOT_MODEL = sys.argv[1] if len(sys.argv) > 1 else \"https://github.com/commaai/openpilot/raw/v0.9.7/selfdrive/modeld/models/supercombo.onnx\"\n", + ), + Context( + "OUTPUT = sys.argv[2] if len(sys.argv) > 2 else \"/tmp/openpilot.pkl\"\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "examples/openpilot/compile4.py", + ), + ), + modified: Some( + Filename( + "examples/openpilot/compile4.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 7, + }, + new_range: HunkRange { + start: 1, + len: 7, + }, + function_context: None, + lines: [ + Context( + "import sys\n", + ), + Context( + "from tinygrad import Tensor, fetch, GlobalCounters, dtypes\n", + ), + Context( + "from tinygrad.uop.ops import UOp\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from tinygrad.schedule.kernelize import get_kernelize_map\n", + ), + Context( + "from tinygrad.schedule.rangeify import get_rangeify_map\n", + ), + Context( + "from tinygrad.helpers import RANGEIFY\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "examples/other_mnist/beautiful_mnist_torch.py", + ), + ), + modified: Some( + Filename( + "examples/other_mnist/beautiful_mnist_torch.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 27, + len: 7, + }, + new_range: HunkRange { + start: 27, + len: 7, + }, + function_context: Some( + "def forward(self, x):\n", + ), + lines: [ + Context( + "\n", + ), + Context( + "if __name__ == \"__main__\":\n", + ), + Context( + " if getenv(\"TINY_BACKEND\"):\n", + ), + Delete( + " import tinygrad.frontend.torch # noqa: F401\n", + ), + Insert( + " import tinygrad.nn.torch # noqa: F401\n", + ), + Context( + " device = torch.device(\"tiny\")\n", + ), + Context( + " else:\n", + ), + Context( + " device = torch.device({\"METAL\":\"mps\",\"NV\":\"cuda\"}.get(Device.DEFAULT, \"cpu\"))\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "examples/yolov8-onnx.py", + ), + ), + modified: Some( + Filename( + "examples/yolov8-onnx.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 2, + len: 7, + }, + new_range: HunkRange { + start: 2, + len: 7, + }, + function_context: None, + lines: [ + Context( + "import os\n", + ), + Context( + "from ultralytics import YOLO\n", + ), + Context( + "from pathlib import Path\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from extra.onnx_helpers import get_example_inputs\n", + ), + Context( + "\n", + ), + Context( + "os.chdir(\"/tmp\")\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "extra/huggingface_onnx/run_models.py", + ), + ), + modified: Some( + Filename( + "extra/huggingface_onnx/run_models.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 6, + }, + new_range: HunkRange { + start: 1, + len: 6, + }, + function_context: None, + lines: [ + Context( + "import onnx, yaml, tempfile, time, argparse, json\n", + ), + Context( + "from pathlib import Path\n", + ), + Context( + "from typing import Any\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from extra.onnx_helpers import validate, get_example_inputs\n", + ), + Context( + "from extra.huggingface_onnx.huggingface_manager import DOWNLOADS_DIR, snapshot_download_with_retry\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "extra/onnx_helpers.py", + ), + ), + modified: Some( + Filename( + "extra/onnx_helpers.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 5, + }, + new_range: HunkRange { + start: 1, + len: 5, + }, + function_context: None, + lines: [ + Context( + "from tinygrad import Tensor\n", + ), + Context( + "from tinygrad.tensor import _to_np_dtype\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner, OnnxValue\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner, OnnxValue\n", + ), + Context( + "import numpy as np\n", + ), + Context( + "import onnxruntime as ort\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "extra/torch_backend/test_inplace.py", + ), + ), + modified: Some( + Filename( + "extra/torch_backend/test_inplace.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 5, + }, + new_range: HunkRange { + start: 1, + len: 5, + }, + function_context: None, + lines: [ + Context( + "import unittest\n", + ), + Context( + "import torch\n", + ), + Delete( + "import tinygrad.frontend.torch\n", + ), + Insert( + "import tinygrad.nn.torch\n", + ), + Context( + "torch.set_default_device(\"tiny\")\n", + ), + Context( + "import numpy as np\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "extra/torch_backend/test_multigpu.py", + ), + ), + modified: Some( + Filename( + "extra/torch_backend/test_multigpu.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 6, + }, + new_range: HunkRange { + start: 1, + len: 6, + }, + function_context: None, + lines: [ + Context( + "import unittest\n", + ), + Context( + "from tinygrad.helpers import getenv\n", + ), + Context( + "import torch\n", + ), + Delete( + "import tinygrad.frontend.torch\n", + ), + Insert( + "import tinygrad.nn.torch\n", + ), + Context( + "torch.set_default_device(\"tiny\")\n", + ), + Context( + "import numpy as np\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "setup.py", + ), + ), + modified: Some( + Filename( + "setup.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 31, + len: 7, + }, + new_range: HunkRange { + start: 31, + len: 6, + }, + function_context: None, + lines: [ + Context( + " 'tinygrad.codegen.opt',\n", + ), + Context( + " 'tinygrad.codegen.late',\n", + ), + Context( + " 'tinygrad.engine',\n", + ), + Delete( + " 'tinygrad.frontend',\n", + ), + Context( + " 'tinygrad.nn',\n", + ), + Context( + " 'tinygrad.renderer',\n", + ), + Context( + " 'tinygrad.runtime',\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/external/external_benchmark_openpilot.py", + ), + ), + modified: Some( + Filename( + "test/external/external_benchmark_openpilot.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 6, + }, + new_range: HunkRange { + start: 1, + len: 6, + }, + function_context: None, + lines: [ + Context( + "import time, sys, hashlib\n", + ), + Context( + "from pathlib import Path\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from tinygrad import Tensor, dtypes, TinyJit\n", + ), + Context( + "from tinygrad.helpers import IMAGE, GlobalCounters, fetch, colored, getenv, trange\n", + ), + Context( + "import numpy as np\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/external/external_model_benchmark.py", + ), + ), + modified: Some( + Filename( + "test/external/external_model_benchmark.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 4, + len: 7, + }, + new_range: HunkRange { + start: 4, + len: 7, + }, + function_context: None, + lines: [ + Context( + "torch.set_num_threads(1)\n", + ), + Context( + "import onnxruntime as ort\n", + ), + Context( + "from onnx2torch import convert\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from tinygrad.helpers import OSX, DEBUG, fetch, getenv\n", + ), + Context( + "from tinygrad.dtype import _to_np_dtype\n", + ), + Context( + "from tinygrad import Tensor, Device, dtypes\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/external/external_test_onnx_backend.py", + ), + ), + modified: Some( + Filename( + "test/external/external_test_onnx_backend.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 6, + len: 7, + }, + new_range: HunkRange { + start: 6, + len: 7, + }, + function_context: None, + lines: [ + Context( + "from tinygrad import Tensor, Device, dtypes\n", + ), + Context( + "from tinygrad.helpers import getenv, OSX\n", + ), + Context( + "from tinygrad.device import is_dtype_supported\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "\n", + ), + Context( + "# pip3 install tabulate\n", + ), + Context( + "pytest_plugins = 'onnx.backend.test.report',\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/external/external_test_onnx_ops.py", + ), + ), + modified: Some( + Filename( + "test/external/external_test_onnx_ops.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 5, + len: 7, + }, + new_range: HunkRange { + start: 5, + len: 7, + }, + function_context: None, + lines: [ + Context( + "from typing import Any\n", + ), + Context( + "import unittest, onnx, tempfile\n", + ), + Context( + "from tinygrad import dtypes, Tensor\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "import numpy as np\n", + ), + Context( + "from extra.onnx_helpers import validate\n", + ), + Context( + "from onnx.defs import ONNX_DOMAIN, AI_ONNX_PREVIEW_TRAINING_DOMAIN\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/external/external_test_onnx_runner.py", + ), + ), + modified: Some( + Filename( + "test/external/external_test_onnx_runner.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 3, + len: 7, + }, + new_range: HunkRange { + start: 3, + len: 7, + }, + function_context: None, + lines: [ + Context( + "from tinygrad import dtypes, Tensor\n", + ), + Context( + "from tinygrad.uop.ops import Ops\n", + ), + Context( + "from tinygrad.device import is_dtype_supported\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner, OnnxDataType\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner, OnnxDataType\n", + ), + Context( + "from hypothesis import given, strategies as st\n", + ), + Context( + "\n", + ), + Context( + "# copied from test_const_folding.py\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/models/test_onnx.py", + ), + ), + modified: Some( + Filename( + "test/models/test_onnx.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 1, + len: 6, + }, + new_range: HunkRange { + start: 1, + len: 6, + }, + function_context: None, + lines: [ + Context( + "#!/usr/bin/env python\n", + ), + Context( + "import unittest\n", + ), + Context( + "import numpy as np\n", + ), + Delete( + "from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + "from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + "from tinygrad.device import Device\n", + ), + Context( + "from tinygrad.helpers import fetch, Context\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/test_ops.py", + ), + ), + modified: Some( + Filename( + "test/test_ops.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 8, + len: 7, + }, + new_range: HunkRange { + start: 8, + len: 7, + }, + function_context: None, + lines: [ + Context( + "from tinygrad.device import is_dtype_supported\n", + ), + Context( + "\n", + ), + Context( + "if getenv(\"TINY_BACKEND\"):\n", + ), + Delete( + " import tinygrad.frontend.torch # noqa: F401 # pylint: disable=unused-import\n", + ), + Insert( + " import tinygrad.nn.torch # noqa: F401 # pylint: disable=unused-import\n", + ), + Context( + " torch.set_default_device(\"tiny\")\n", + ), + Context( + "\n", + ), + Context( + "if CI:\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "test/test_quantize_onnx.py", + ), + ), + modified: Some( + Filename( + "test/test_quantize_onnx.py", + ), + ), + hunks: [ + Hunk { + old_range: HunkRange { + start: 68, + len: 7, + }, + new_range: HunkRange { + start: 68, + len: 7, + }, + function_context: Some( + "def test_quant_128(self, sz=128):\n", + ), + lines: [ + Context( + " import onnx # noqa: F401 # pylint: disable=unused-import\n", + ), + Context( + " except ImportError:\n", + ), + Context( + " raise unittest.SkipTest()\n", + ), + Delete( + " from tinygrad.frontend.onnx import OnnxRunner\n", + ), + Insert( + " from tinygrad.nn.onnx import OnnxRunner\n", + ), + Context( + " out_file = get_quantized_model(sz)\n", + ), + Context( + " run_onnx = OnnxRunner(out_file)\n", + ), + Context( + " inp = Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32))\n", + ), + ], + }, + ], + }, + Patch { + original: Some( + Filename( + "tinygrad/frontend/__init__.py", + ), + ), + modified: Some( + Filename( + "tinygrad/frontend/__init__.py", + ), + ), + hunks: [], + }, + Patch { + original: Some( + Filename( + "tinygrad/frontend/onnx.py", + ), + ), + modified: Some( + Filename( + "tinygrad/nn/onnx.py", + ), + ), + hunks: [], + }, + Patch { + original: Some( + Filename( + "tinygrad/frontend/torch.py", + ), + ), + modified: Some( + Filename( + "tinygrad/nn/torch.py", + ), + ), + hunks: [], + }, + ], +) diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@simple.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@simple.patch.snap index 4da6821..d1b676b 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@simple.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@simple.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/hello.txt", + "hello.txt", ), ), modified: Some( Filename( - "b/hello.txt", + "hello.txt", ), ), hunks: [ diff --git a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@windows_lib_so_name.patch.snap b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@windows_lib_so_name.patch.snap index db46f25..1077a5f 100644 --- a/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@windows_lib_so_name.patch.snap +++ b/src/patch/snapshots/diffy__patch__parse__tests__real_world_patches@windows_lib_so_name.patch.snap @@ -8,12 +8,12 @@ Ok( Patch { original: Some( Filename( - "a/src/core/CMakeLists.txt", + "src/core/CMakeLists.txt", ), ), modified: Some( Filename( - "b/src/core/CMakeLists.txt", + "src/core/CMakeLists.txt", ), ), hunks: [ diff --git a/src/patch/test-data/pure-renames.patch b/src/patch/test-data/pure-renames.patch new file mode 100644 index 0000000..515bfb7 --- /dev/null +++ b/src/patch/test-data/pure-renames.patch @@ -0,0 +1,286 @@ +From 0f25b4b28954df4ea8ed489acbaa31265a1fafbe Mon Sep 17 00:00:00 2001 +From: George Hotz <72895+geohot@users.noreply.github.com> +Date: Tue, 7 Oct 2025 10:42:22 +0800 +Subject: [PATCH] move frontend dir to nn [pr] (#12470) + +--- + examples/benchmark_onnx.py | 2 +- + examples/compile_tensorflow.py | 2 +- + examples/openpilot/compile3.py | 2 +- + examples/openpilot/compile4.py | 2 +- + examples/other_mnist/beautiful_mnist_torch.py | 2 +- + examples/yolov8-onnx.py | 2 +- + extra/huggingface_onnx/run_models.py | 2 +- + extra/onnx_helpers.py | 2 +- + extra/torch_backend/test_inplace.py | 2 +- + extra/torch_backend/test_multigpu.py | 2 +- + setup.py | 1 - + test/external/external_benchmark_openpilot.py | 2 +- + test/external/external_model_benchmark.py | 2 +- + test/external/external_test_onnx_backend.py | 2 +- + test/external/external_test_onnx_ops.py | 2 +- + test/external/external_test_onnx_runner.py | 2 +- + test/models/test_onnx.py | 2 +- + test/test_ops.py | 2 +- + test/test_quantize_onnx.py | 2 +- + tinygrad/frontend/__init__.py | 0 + tinygrad/{frontend => nn}/onnx.py | 0 + tinygrad/{frontend => nn}/torch.py | 0 + 22 files changed, 18 insertions(+), 19 deletions(-) + delete mode 100644 tinygrad/frontend/__init__.py + rename tinygrad/{frontend => nn}/onnx.py (100%) + rename tinygrad/{frontend => nn}/torch.py (100%) + +diff --git a/examples/benchmark_onnx.py b/examples/benchmark_onnx.py +index ad7c1ebb18ecd..27568117f32f6 100644 +--- a/examples/benchmark_onnx.py ++++ b/examples/benchmark_onnx.py +@@ -1,6 +1,6 @@ + import sys, time + from tinygrad import TinyJit, GlobalCounters, fetch, getenv +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from extra.onnx_helpers import get_example_inputs, validate + + def load_onnx_model(onnx_file): +diff --git a/examples/compile_tensorflow.py b/examples/compile_tensorflow.py +index 33434c831cac3..1962661818890 100644 +--- a/examples/compile_tensorflow.py ++++ b/examples/compile_tensorflow.py +@@ -8,7 +8,7 @@ + import subprocess + import tensorflow as tf + import tf2onnx +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from tinygrad.tensor import Tensor + from tinygrad.helpers import to_mv + from extra.export_model import export_model_clang, compile_net, jit_model +diff --git a/examples/openpilot/compile3.py b/examples/openpilot/compile3.py +index 6159bca5d62bc..1eb4d1f46f188 100644 +--- a/examples/openpilot/compile3.py ++++ b/examples/openpilot/compile3.py +@@ -10,7 +10,7 @@ + from tinygrad.engine.realize import CompiledRunner + + import onnx +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + + OPENPILOT_MODEL = sys.argv[1] if len(sys.argv) > 1 else "https://github.com/commaai/openpilot/raw/v0.9.7/selfdrive/modeld/models/supercombo.onnx" + OUTPUT = sys.argv[2] if len(sys.argv) > 2 else "/tmp/openpilot.pkl" +diff --git a/examples/openpilot/compile4.py b/examples/openpilot/compile4.py +index 9f363ff2eac7b..3c13c58d460d0 100644 +--- a/examples/openpilot/compile4.py ++++ b/examples/openpilot/compile4.py +@@ -1,7 +1,7 @@ + import sys + from tinygrad import Tensor, fetch, GlobalCounters, dtypes + from tinygrad.uop.ops import UOp +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from tinygrad.schedule.kernelize import get_kernelize_map + from tinygrad.schedule.rangeify import get_rangeify_map + from tinygrad.helpers import RANGEIFY +diff --git a/examples/other_mnist/beautiful_mnist_torch.py b/examples/other_mnist/beautiful_mnist_torch.py +index 9fa597bae86ad..8e0b7dd64d27a 100644 +--- a/examples/other_mnist/beautiful_mnist_torch.py ++++ b/examples/other_mnist/beautiful_mnist_torch.py +@@ -27,7 +27,7 @@ def forward(self, x): + + if __name__ == "__main__": + if getenv("TINY_BACKEND"): +- import tinygrad.frontend.torch # noqa: F401 ++ import tinygrad.nn.torch # noqa: F401 + device = torch.device("tiny") + else: + device = torch.device({"METAL":"mps","NV":"cuda"}.get(Device.DEFAULT, "cpu")) +diff --git a/examples/yolov8-onnx.py b/examples/yolov8-onnx.py +index bc3d50ab9ede8..637d3b54e66e7 100644 +--- a/examples/yolov8-onnx.py ++++ b/examples/yolov8-onnx.py +@@ -2,7 +2,7 @@ + import os + from ultralytics import YOLO + from pathlib import Path +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from extra.onnx_helpers import get_example_inputs + + os.chdir("/tmp") +diff --git a/extra/huggingface_onnx/run_models.py b/extra/huggingface_onnx/run_models.py +index fa8771a11afee..2989c58e74f3f 100644 +--- a/extra/huggingface_onnx/run_models.py ++++ b/extra/huggingface_onnx/run_models.py +@@ -1,7 +1,7 @@ + import onnx, yaml, tempfile, time, argparse, json + from pathlib import Path + from typing import Any +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from extra.onnx_helpers import validate, get_example_inputs + from extra.huggingface_onnx.huggingface_manager import DOWNLOADS_DIR, snapshot_download_with_retry + +diff --git a/extra/onnx_helpers.py b/extra/onnx_helpers.py +index 632d5df8d726a..73a88da0b4444 100644 +--- a/extra/onnx_helpers.py ++++ b/extra/onnx_helpers.py +@@ -1,6 +1,6 @@ + from tinygrad import Tensor + from tinygrad.tensor import _to_np_dtype +-from tinygrad.frontend.onnx import OnnxRunner, OnnxValue ++from tinygrad.nn.onnx import OnnxRunner, OnnxValue + import numpy as np + import onnxruntime as ort + +diff --git a/extra/torch_backend/test_inplace.py b/extra/torch_backend/test_inplace.py +index e6f171f05f953..788f8d2eb3891 100644 +--- a/extra/torch_backend/test_inplace.py ++++ b/extra/torch_backend/test_inplace.py +@@ -1,6 +1,6 @@ + import unittest + import torch +-import tinygrad.frontend.torch ++import tinygrad.nn.torch + torch.set_default_device("tiny") + import numpy as np + +diff --git a/extra/torch_backend/test_multigpu.py b/extra/torch_backend/test_multigpu.py +index 9a21898132c7b..cff18bf2afe90 100644 +--- a/extra/torch_backend/test_multigpu.py ++++ b/extra/torch_backend/test_multigpu.py +@@ -1,7 +1,7 @@ + import unittest + from tinygrad.helpers import getenv + import torch +-import tinygrad.frontend.torch ++import tinygrad.nn.torch + torch.set_default_device("tiny") + import numpy as np + +diff --git a/setup.py b/setup.py +index f90a52b5845a1..39dd40da60481 100644 +--- a/setup.py ++++ b/setup.py +@@ -31,7 +31,6 @@ + 'tinygrad.codegen.opt', + 'tinygrad.codegen.late', + 'tinygrad.engine', +- 'tinygrad.frontend', + 'tinygrad.nn', + 'tinygrad.renderer', + 'tinygrad.runtime', +diff --git a/test/external/external_benchmark_openpilot.py b/test/external/external_benchmark_openpilot.py +index 158f41f6a1326..4d097f91d13d3 100644 +--- a/test/external/external_benchmark_openpilot.py ++++ b/test/external/external_benchmark_openpilot.py +@@ -1,6 +1,6 @@ + import time, sys, hashlib + from pathlib import Path +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from tinygrad import Tensor, dtypes, TinyJit + from tinygrad.helpers import IMAGE, GlobalCounters, fetch, colored, getenv, trange + import numpy as np +diff --git a/test/external/external_model_benchmark.py b/test/external/external_model_benchmark.py +index b29892f2d9779..a5ecac462393a 100644 +--- a/test/external/external_model_benchmark.py ++++ b/test/external/external_model_benchmark.py +@@ -4,7 +4,7 @@ + torch.set_num_threads(1) + import onnxruntime as ort + from onnx2torch import convert +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from tinygrad.helpers import OSX, DEBUG, fetch, getenv + from tinygrad.dtype import _to_np_dtype + from tinygrad import Tensor, Device, dtypes +diff --git a/test/external/external_test_onnx_backend.py b/test/external/external_test_onnx_backend.py +index 112ccd797c395..6f6a4fbcb70ea 100644 +--- a/test/external/external_test_onnx_backend.py ++++ b/test/external/external_test_onnx_backend.py +@@ -6,7 +6,7 @@ + from tinygrad import Tensor, Device, dtypes + from tinygrad.helpers import getenv, OSX + from tinygrad.device import is_dtype_supported +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + + # pip3 install tabulate + pytest_plugins = 'onnx.backend.test.report', +diff --git a/test/external/external_test_onnx_ops.py b/test/external/external_test_onnx_ops.py +index e4be34fa5eca8..02b700daa2431 100644 +--- a/test/external/external_test_onnx_ops.py ++++ b/test/external/external_test_onnx_ops.py +@@ -5,7 +5,7 @@ + from typing import Any + import unittest, onnx, tempfile + from tinygrad import dtypes, Tensor +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + import numpy as np + from extra.onnx_helpers import validate + from onnx.defs import ONNX_DOMAIN, AI_ONNX_PREVIEW_TRAINING_DOMAIN +diff --git a/test/external/external_test_onnx_runner.py b/test/external/external_test_onnx_runner.py +index f0d8941b4537e..0b853bc22ed15 100644 +--- a/test/external/external_test_onnx_runner.py ++++ b/test/external/external_test_onnx_runner.py +@@ -3,7 +3,7 @@ + from tinygrad import dtypes, Tensor + from tinygrad.uop.ops import Ops + from tinygrad.device import is_dtype_supported +-from tinygrad.frontend.onnx import OnnxRunner, OnnxDataType ++from tinygrad.nn.onnx import OnnxRunner, OnnxDataType + from hypothesis import given, strategies as st + + # copied from test_const_folding.py +diff --git a/test/models/test_onnx.py b/test/models/test_onnx.py +index 34e5a1320d9b3..34ed658e4316c 100644 +--- a/test/models/test_onnx.py ++++ b/test/models/test_onnx.py +@@ -1,7 +1,7 @@ + #!/usr/bin/env python + import unittest + import numpy as np +-from tinygrad.frontend.onnx import OnnxRunner ++from tinygrad.nn.onnx import OnnxRunner + from tinygrad.device import Device + from tinygrad.helpers import fetch, Context + +diff --git a/test/test_ops.py b/test/test_ops.py +index dbd232edcf80d..952f3a84f0ac3 100644 +--- a/test/test_ops.py ++++ b/test/test_ops.py +@@ -8,7 +8,7 @@ + from tinygrad.device import is_dtype_supported + + if getenv("TINY_BACKEND"): +- import tinygrad.frontend.torch # noqa: F401 # pylint: disable=unused-import ++ import tinygrad.nn.torch # noqa: F401 # pylint: disable=unused-import + torch.set_default_device("tiny") + + if CI: +diff --git a/test/test_quantize_onnx.py b/test/test_quantize_onnx.py +index 005d978902047..cfaa44cc5d7f7 100644 +--- a/test/test_quantize_onnx.py ++++ b/test/test_quantize_onnx.py +@@ -68,7 +68,7 @@ def test_quant_128(self, sz=128): + import onnx # noqa: F401 # pylint: disable=unused-import + except ImportError: + raise unittest.SkipTest() +- from tinygrad.frontend.onnx import OnnxRunner ++ from tinygrad.nn.onnx import OnnxRunner + out_file = get_quantized_model(sz) + run_onnx = OnnxRunner(out_file) + inp = Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32)) +diff --git a/tinygrad/frontend/__init__.py b/tinygrad/frontend/__init__.py +deleted file mode 100644 +index e69de29bb2d1d..0000000000000 +diff --git a/tinygrad/frontend/onnx.py b/tinygrad/nn/onnx.py +similarity index 100% +rename from tinygrad/frontend/onnx.py +rename to tinygrad/nn/onnx.py +diff --git a/tinygrad/frontend/torch.py b/tinygrad/nn/torch.py +similarity index 100% +rename from tinygrad/frontend/torch.py +rename to tinygrad/nn/torch.py \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index d31c626..0a28c2d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ //! Common utilities use std::{ - collections::{hash_map::Entry, HashMap}, + collections::{HashMap, hash_map::Entry}, hash::Hash, };