diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd7da8988..0afa80c58b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### New features +* The following diff formats now include information about copies and moves: + `--color-words`, `--summary` + ### Fixed bugs ## [0.20.0] - 2024-08-07 diff --git a/cli/src/commit_templater.rs b/cli/src/commit_templater.rs index c7cb51fd0e..d6b708401a 100644 --- a/cli/src/commit_templater.rs +++ b/cli/src/commit_templater.rs @@ -1394,8 +1394,15 @@ fn builtin_tree_diff_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, T let path_converter = language.path_converter; let template = self_property .map(move |diff| { + let to_tree = diff.to_tree.clone(); diff.into_formatted(move |formatter, _store, tree_diff| { - diff_util::show_diff_summary(formatter, tree_diff, path_converter) + diff_util::show_diff_summary( + formatter, + tree_diff, + path_converter, + &Default::default(), + &to_tree, + ) }) }) .into_template(); diff --git a/cli/src/diff_util.rs b/cli/src/diff_util.rs index 1379547352..1f14cb71aa 100644 --- a/cli/src/diff_util.rs +++ b/cli/src/diff_util.rs @@ -13,7 +13,7 @@ // limitations under the License. use std::cmp::max; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; use std::ops::Range; use std::path::{Path, PathBuf}; use std::{io, mem}; @@ -272,7 +272,7 @@ impl<'a> DiffRenderer<'a> { from_tree: &MergedTree, to_tree: &MergedTree, matcher: &dyn Matcher, - _copy_records: &CopyRecords, + copy_records: &CopyRecords, width: usize, ) -> Result<(), DiffRenderError> { let store = self.repo.store(); @@ -280,9 +280,8 @@ impl<'a> DiffRenderer<'a> { for format in &self.formats { match format { DiffFormat::Summary => { - let no_copy_tracking = Default::default(); - let tree_diff = from_tree.diff_stream(to_tree, matcher, &no_copy_tracking); - show_diff_summary(formatter, tree_diff, path_converter)?; + let tree_diff = from_tree.diff_stream(to_tree, matcher, copy_records); + show_diff_summary(formatter, tree_diff, path_converter, copy_records, to_tree)?; } DiffFormat::Stat => { let no_copy_tracking = Default::default(); @@ -305,8 +304,7 @@ impl<'a> DiffRenderer<'a> { show_git_diff(formatter, store, tree_diff, *context)?; } DiffFormat::ColorWords { context } => { - let no_copy_tracking = Default::default(); - let tree_diff = from_tree.diff_stream(to_tree, matcher, &no_copy_tracking); + let tree_diff = from_tree.diff_stream(to_tree, matcher, copy_records); show_color_words_diff(formatter, store, tree_diff, path_converter, *context)?; } DiffFormat::Tool(tool) => { @@ -580,20 +578,28 @@ pub fn show_color_words_diff( let mut diff_stream = materialized_diff_stream(store, tree_diff); async { while let Some(MaterializedTreeDiffEntry { - source: _, // TODO handle copy tracking - target: path, + source: left_path, + target: right_path, value: diff, }) = diff_stream.next().await { - let ui_path = path_converter.format_file_path(&path); + let left_ui_path = path_converter.format_file_path(&left_path); + let right_ui_path = path_converter.format_file_path(&right_path); let (left_value, right_value) = diff?; match (&left_value, &right_value) { - (_, MaterializedTreeValue::AccessDenied(source)) - | (MaterializedTreeValue::AccessDenied(source), _) => { + (MaterializedTreeValue::AccessDenied(source), _) => { write!( formatter.labeled("access-denied"), - "Access denied to {ui_path}:" + "Access denied to {left_ui_path}:" + )?; + writeln!(formatter, " {source}")?; + continue; + } + (_, MaterializedTreeValue::AccessDenied(source)) => { + write!( + formatter.labeled("access-denied"), + "Access denied to {right_ui_path}:" )?; writeln!(formatter, " {source}")?; continue; @@ -604,9 +610,9 @@ pub fn show_color_words_diff( let description = basic_diff_file_type(&right_value); writeln!( formatter.labeled("header"), - "Added {description} {ui_path}:" + "Added {description} {right_ui_path}:" )?; - let right_content = diff_content(&path, right_value)?; + let right_content = diff_content(&right_path, right_value)?; if right_content.is_empty() { writeln!(formatter.labeled("empty"), " (empty)")?; } else if right_content.is_binary { @@ -667,9 +673,19 @@ pub fn show_color_words_diff( ) } }; - let left_content = diff_content(&path, left_value)?; - let right_content = diff_content(&path, right_value)?; - writeln!(formatter.labeled("header"), "{description} {ui_path}:")?; + let left_content = diff_content(&left_path, left_value)?; + let right_content = diff_content(&right_path, right_value)?; + if left_path == right_path { + writeln!( + formatter.labeled("header"), + "{description} {right_ui_path}:" + )?; + } else { + writeln!( + formatter.labeled("header"), + "{description} {right_ui_path} ({left_ui_path} => {right_ui_path}):" + )?; + } if left_content.is_binary || right_content.is_binary { writeln!(formatter.labeled("binary"), " (binary)")?; } else { @@ -684,9 +700,9 @@ pub fn show_color_words_diff( let description = basic_diff_file_type(&left_value); writeln!( formatter.labeled("header"), - "Removed {description} {ui_path}:" + "Removed {description} {right_ui_path}:" )?; - let left_content = diff_content(&path, left_value)?; + let left_content = diff_content(&left_path, left_value)?; if left_content.is_empty() { writeln!(formatter.labeled("empty"), " (empty)")?; } else if left_content.is_binary { @@ -1130,33 +1146,60 @@ pub fn show_git_diff( .block_on() } +// TODO rework this signature to pass both from_tree and to_tree explicitly #[instrument(skip_all)] pub fn show_diff_summary( formatter: &mut dyn Formatter, mut tree_diff: TreeDiffStream, path_converter: &RepoPathUiConverter, + copy_records: &CopyRecords, + to_tree: &MergedTree, ) -> io::Result<()> { + let copied_sources: HashSet<&RepoPath> = copy_records + .iter() + .map(|record| record.source.as_ref()) + .collect(); + async { while let Some(TreeDiffEntry { - source: _, // TODO handle copy tracking - target: repo_path, + source: before_path, + target: after_path, value: diff, }) = tree_diff.next().await { let (before, after) = diff.unwrap(); - let ui_path = path_converter.format_file_path(&repo_path); - if before.is_present() && after.is_present() { - writeln!(formatter.labeled("modified"), "M {ui_path}")?; - } else if before.is_absent() { - writeln!(formatter.labeled("added"), "A {ui_path}")?; + let after_ui_path = path_converter.format_file_path(&after_path); + if before_path != after_path { + let before_ui_path = path_converter.format_file_path(&before_path); + if to_tree.path_value(&before_path).unwrap().is_absent() { + writeln!( + formatter.labeled("renamed"), + "R {before_ui_path} => {after_ui_path}" + )? + } else { + writeln!( + formatter.labeled("copied"), + "C {before_ui_path} => {after_ui_path}" + )? + } } else { - // `R` could be interpreted as "renamed" - writeln!(formatter.labeled("removed"), "D {ui_path}")?; + let path = after_ui_path; + match (before.is_present(), after.is_present()) { + (true, true) => writeln!(formatter.labeled("modified"), "M {path}")?, + (false, true) => writeln!(formatter.labeled("added"), "A {path}")?, + (true, false) => { + if !copied_sources.contains(before_path.as_ref()) { + writeln!(formatter.labeled("removed"), "D {path}")?; + } + } + (false, false) => unreachable!(), + } } } - Ok(()) + Ok::<(), io::Error>(()) } - .block_on() + .block_on()?; + Ok(()) } struct DiffStat { diff --git a/cli/tests/test_diff_command.rs b/cli/tests/test_diff_command.rs index b9f89c4e4e..63b8af6df9 100644 --- a/cli/tests/test_diff_command.rs +++ b/cli/tests/test_diff_command.rs @@ -37,8 +37,7 @@ fn test_diff_basic() { 1 1: foo 2: bar 2 3: baz quxquux - Added regular file file3: - 1: foo + Modified regular file file3 (file1 => file3): "###); let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--context=0"]); @@ -49,8 +48,7 @@ fn test_diff_basic() { 1 1: foo 2: bar 2 3: baz quxquux - Added regular file file3: - 1: foo + Modified regular file file3 (file1 => file3): "###); let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--color=debug"]); @@ -61,15 +59,13 @@ fn test_diff_basic() { <><><><> <><><><> <><><><><><><> - <> - <><><><> + < file3):>> "###); let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); insta::assert_snapshot!(stdout, @r###" - D file1 M file2 - A file3 + R file1 => file3 "###); let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--types"]); @@ -161,9 +157,8 @@ fn test_diff_basic() { let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "--git"]); insta::assert_snapshot!(stdout, @r###" - D file1 M file2 - A file3 + R file1 => file3 diff --git a/file1 b/file1 deleted file mode 100644 index 257cc5642c..0000000000 @@ -200,7 +195,6 @@ fn test_diff_basic() { // Filter by glob pattern let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "glob:file[12]"]); insta::assert_snapshot!(stdout, @r###" - D file1 M file2 "###); @@ -218,9 +212,8 @@ fn test_diff_basic() { ], ); insta::assert_snapshot!(stdout.replace('\\', "/"), @r###" - D repo/file1 M repo/file2 - A repo/file3 + R repo/file1 => repo/file3 "###); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Warning: No matching entries for paths: repo/x, repo/y/z @@ -1253,12 +1246,12 @@ fn test_diff_external_file_by_file_tool() { test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); let repo_path = test_env.env_root().join("repo"); - std::fs::write(repo_path.join("file1"), "foo\n").unwrap(); - std::fs::write(repo_path.join("file2"), "foo\n").unwrap(); + std::fs::write(repo_path.join("file1"), "file1\n").unwrap(); + std::fs::write(repo_path.join("file2"), "file2\n").unwrap(); test_env.jj_cmd_ok(&repo_path, &["new"]); std::fs::remove_file(repo_path.join("file1")).unwrap(); - std::fs::write(repo_path.join("file2"), "foo\nbar\n").unwrap(); - std::fs::write(repo_path.join("file3"), "foo\n").unwrap(); + std::fs::write(repo_path.join("file2"), "file2\nfile2\n").unwrap(); + std::fs::write(repo_path.join("file3"), "file3\n").unwrap(); let edit_script = test_env.set_up_fake_diff_editor(); std::fs::write( @@ -1299,7 +1292,7 @@ fn test_diff_external_file_by_file_tool() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-p", config]), @r###" - @ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 39d9055d + @ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 f12f62fb │ (no description set) │ == │ file1 @@ -1313,7 +1306,7 @@ fn test_diff_external_file_by_file_tool() { │ file3 │ -- │ file3 - ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 0ad4ef22 + ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 6e485984 │ (no description set) │ == │ file1 @@ -1328,7 +1321,7 @@ fn test_diff_external_file_by_file_tool() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["show", config]), @r###" - Commit ID: 39d9055d70873099fd924b9af218289d5663eac8 + Commit ID: f12f62fb1d73629c1b44abd4d5bbb500d7f8b86c Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp Author: Test User (2001-02-03 08:05:09) Committer: Test User (2001-02-03 08:05:09) diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 75c75b0692..12f5cebee3 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -217,6 +217,11 @@ impl CopyRecords { pub fn for_target(&self, target: &RepoPath) -> Option<&CopyRecord> { self.targets.get(target).and_then(|&i| self.records.get(i)) } + + /// Gets all copy records. + pub fn iter(&self) -> impl Iterator + '_ { + self.records.iter() + } } /// Error that may occur during backend initialization. diff --git a/lib/src/merged_tree.rs b/lib/src/merged_tree.rs index c4540b110d..9c3d8d1401 100644 --- a/lib/src/merged_tree.rs +++ b/lib/src/merged_tree.rs @@ -29,7 +29,7 @@ use futures::{Stream, TryStreamExt}; use itertools::{EitherOrBoth, Itertools}; use crate::backend; -use crate::backend::{BackendResult, CopyRecords, MergedTreeId, TreeId, TreeValue}; +use crate::backend::{BackendResult, CopyRecord, CopyRecords, MergedTreeId, TreeId, TreeValue}; use crate::matchers::{EverythingMatcher, Matcher}; use crate::merge::{Merge, MergeBuilder, MergedTreeValue}; use crate::repo_path::{RepoPath, RepoPathBuf, RepoPathComponent}; @@ -334,6 +334,28 @@ pub struct TreeDiffEntry { pub value: BackendResult<(MergedTreeValue, MergedTreeValue)>, } +impl TreeDiffEntry { + fn adjust_for_copy_tracking( + self, + source_tree: &MergedTree, + copy_records: &CopyRecords, + ) -> TreeDiffEntry { + let Some(CopyRecord { source, .. }) = copy_records.for_target(&self.target) else { + return self; + }; + + Self { + source: source.clone(), + target: self.target, + value: self.value.and_then(|(_, target_value)| { + source_tree + .path_value(source) + .map(|source_value| (source_value, target_value)) + }), + } + } +} + /// Type alias for the result from `MergedTree::diff_stream()`. We use a /// `Stream` instead of an `Iterator` so high-latency backends (e.g. cloud-based /// ones) can fetch trees asynchronously. @@ -612,7 +634,9 @@ impl Iterator for ConflictIterator { pub struct TreeDiffIterator<'matcher> { store: Arc, stack: Vec, + source: MergedTree, matcher: &'matcher dyn Matcher, + copy_records: &'matcher CopyRecords, } struct TreeDiffDirItem { @@ -634,7 +658,7 @@ impl<'matcher> TreeDiffIterator<'matcher> { trees1: &Merge, trees2: &Merge, matcher: &'matcher dyn Matcher, - _copy_records: &'matcher CopyRecords, + copy_records: &'matcher CopyRecords, ) -> Self { assert!(Arc::ptr_eq(trees1.first().store(), trees2.first().store())); let root_dir = RepoPath::root(); @@ -647,7 +671,9 @@ impl<'matcher> TreeDiffIterator<'matcher> { Self { store: trees1.first().store().clone(), stack, + source: MergedTree::new(trees1.clone()), matcher, + copy_records, } } @@ -789,12 +815,15 @@ impl Iterator for TreeDiffIterator<'_> { fn next(&mut self) -> Option { self.next_impl() + .map(|diff_entry| diff_entry.adjust_for_copy_tracking(&self.source, self.copy_records)) } } /// Stream of differences between two trees. pub struct TreeDiffStreamImpl<'matcher> { matcher: &'matcher dyn Matcher, + copy_records: &'matcher CopyRecords, + source_tree: MergedTree, /// Pairs of tree values that may or may not be ready to emit, sorted in the /// order we want to emit them. If either side is a tree, there will be /// a corresponding entry in `pending_trees`. @@ -869,11 +898,13 @@ impl<'matcher> TreeDiffStreamImpl<'matcher> { tree1: MergedTree, tree2: MergedTree, matcher: &'matcher dyn Matcher, - _copy_records: &'matcher CopyRecords, + copy_records: &'matcher CopyRecords, max_concurrent_reads: usize, ) -> Self { let mut stream = Self { matcher, + copy_records, + source_tree: tree1.clone(), items: BTreeMap::new(), pending_trees: VecDeque::new(), max_concurrent_reads, @@ -1061,7 +1092,11 @@ impl Stream for TreeDiffStreamImpl<'_> { type Item = TreeDiffEntry; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.as_mut().poll_next_impl(cx) + self.as_mut().poll_next_impl(cx).map(|option| { + option.map(|diff_entry| { + diff_entry.adjust_for_copy_tracking(&self.source_tree, self.copy_records) + }) + }) } } diff --git a/lib/tests/test_merged_tree.rs b/lib/tests/test_merged_tree.rs index b8196c33c1..cfe4217c68 100644 --- a/lib/tests/test_merged_tree.rs +++ b/lib/tests/test_merged_tree.rs @@ -14,7 +14,7 @@ use futures::StreamExt; use itertools::Itertools; -use jj_lib::backend::{FileId, MergedTreeId, TreeValue}; +use jj_lib::backend::{CommitId, CopyRecord, CopyRecords, FileId, MergedTreeId, TreeValue}; use jj_lib::files::MergeResult; use jj_lib::matchers::{EverythingMatcher, FilesMatcher, Matcher, PrefixMatcher}; use jj_lib::merge::{Merge, MergeBuilder, MergedTreeValue}; @@ -39,21 +39,25 @@ fn diff_entry_tuple(diff: TreeDiffEntry) -> (RepoPathBuf, (MergedTreeValue, Merg (diff.target, diff.value.unwrap()) } -fn diff_stream_equals_iter(tree1: &MergedTree, tree2: &MergedTree, matcher: &dyn Matcher) { - let copy_records = Default::default(); +fn diff_stream_equals_iter( + tree1: &MergedTree, + tree2: &MergedTree, + matcher: &dyn Matcher, + copy_records: &CopyRecords, +) { let iter_diff: Vec<_> = - TreeDiffIterator::new(tree1.as_merge(), tree2.as_merge(), matcher, ©_records) - .map(diff_entry_tuple) + TreeDiffIterator::new(tree1.as_merge(), tree2.as_merge(), matcher, copy_records) + .map(|diff| (diff.source, diff.target, diff.value.unwrap())) .collect(); let max_concurrent_reads = 10; let stream_diff: Vec<_> = TreeDiffStreamImpl::new( tree1.clone(), tree2.clone(), matcher, - ©_records, + copy_records, max_concurrent_reads, ) - .map(diff_entry_tuple) + .map(|diff| (diff.source, diff.target, diff.value.unwrap())) .collect() .block_on(); assert_eq!(stream_diff, iter_diff); @@ -794,7 +798,122 @@ fn test_diff_resolved() { ), ) ); - diff_stream_equals_iter(&before_merged, &after_merged, &EverythingMatcher); + diff_stream_equals_iter( + &before_merged, + &after_merged, + &EverythingMatcher, + &CopyRecords::default(), + ); +} + +fn create_copy_records(paths: &[(&RepoPath, &RepoPath)]) -> CopyRecords { + let mut copy_records = CopyRecords::default(); + copy_records + .add_records(Box::pin(futures::stream::iter(paths.iter().map( + |&(source, target)| { + Ok(CopyRecord { + source: source.to_owned(), + target: target.to_owned(), + target_commit: CommitId::new(vec![]), + source_commit: CommitId::new(vec![]), + source_file: FileId::new(vec![]), + }) + }, + )))) + .unwrap(); + copy_records +} + +/// Diff two resolved trees +#[test] +fn test_diff_copy_tracing() { + let test_repo = TestRepo::init(); + let repo = &test_repo.repo; + + let clean_path = RepoPath::from_internal_string("1/clean/path"); + let modified_path = RepoPath::from_internal_string("2/modified/path"); + let copied_path = RepoPath::from_internal_string("3/copied/path"); + let removed_path = RepoPath::from_internal_string("4/removed/path"); + let added_path = RepoPath::from_internal_string("5/added/path"); + let before = create_single_tree( + repo, + &[ + (clean_path, "clean"), + (modified_path, "before"), + (removed_path, "before"), + ], + ); + let after = create_single_tree( + repo, + &[ + (clean_path, "clean"), + (modified_path, "after"), + (copied_path, "after"), + (added_path, "after"), + ], + ); + let before_merged = MergedTree::new(Merge::resolved(before.clone())); + let after_merged = MergedTree::new(Merge::resolved(after.clone())); + + let copy_records = + create_copy_records(&[(removed_path, added_path), (modified_path, copied_path)]); + + let diff: Vec<_> = before_merged + .diff_stream(&after_merged, &EverythingMatcher, ©_records) + .map(|diff| (diff.source, diff.target, diff.value.unwrap())) + .collect() + .block_on(); + assert_eq!(diff.len(), 4); + assert_eq!( + diff[0].clone(), + ( + modified_path.to_owned(), + modified_path.to_owned(), + ( + Merge::resolved(before.path_value(modified_path).unwrap()), + Merge::resolved(after.path_value(modified_path).unwrap()) + ), + ) + ); + assert_eq!( + diff[1].clone(), + ( + modified_path.to_owned(), + copied_path.to_owned(), + ( + Merge::resolved(before.path_value(modified_path).unwrap()), + Merge::resolved(after.path_value(copied_path).unwrap()), + ), + ) + ); + assert_eq!( + diff[2].clone(), + ( + removed_path.to_owned(), + removed_path.to_owned(), + ( + Merge::resolved(before.path_value(removed_path).unwrap()), + Merge::absent() + ), + ) + ); + assert_eq!( + diff[3].clone(), + ( + removed_path.to_owned(), + added_path.to_owned(), + ( + Merge::resolved(before.path_value(removed_path).unwrap()), + Merge::resolved(after.path_value(added_path).unwrap()) + ), + ) + ); + diff_stream_equals_iter( + &before_merged, + &after_merged, + &EverythingMatcher, + ©_records, + ); } /// Diff two conflicted trees @@ -886,7 +1005,12 @@ fn test_diff_conflicted() { }) .collect_vec(); assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &EverythingMatcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &EverythingMatcher, + &CopyRecords::default(), + ); // Test the reverse diff let actual_diff: Vec<_> = right_merged .diff_stream(&left_merged, &EverythingMatcher, &Default::default()) @@ -906,7 +1030,12 @@ fn test_diff_conflicted() { }) .collect_vec(); assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&right_merged, &left_merged, &EverythingMatcher); + diff_stream_equals_iter( + &right_merged, + &left_merged, + &EverythingMatcher, + &CopyRecords::default(), + ); } #[test] @@ -1046,7 +1175,12 @@ fn test_diff_dir_file() { (path6.to_owned(), (Merge::absent(), right_value(path6))), ]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &EverythingMatcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &EverythingMatcher, + &CopyRecords::default(), + ); } // Test the reverse diff @@ -1091,7 +1225,12 @@ fn test_diff_dir_file() { ), ]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&right_merged, &left_merged, &EverythingMatcher); + diff_stream_equals_iter( + &right_merged, + &left_merged, + &EverythingMatcher, + &CopyRecords::default(), + ); } // Diff while filtering by `path1` (file1 -> directory1) as a file @@ -1107,7 +1246,12 @@ fn test_diff_dir_file() { (path1.to_owned(), (left_value(path1), Merge::absent())), ]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &matcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &matcher, + &CopyRecords::default(), + ); } // Diff while filtering by `path1/file` (file1 -> directory1) as a file @@ -1126,7 +1270,12 @@ fn test_diff_dir_file() { ), ]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &matcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &matcher, + &CopyRecords::default(), + ); } // Diff while filtering by `path1` (file1 -> directory1) as a prefix @@ -1145,7 +1294,12 @@ fn test_diff_dir_file() { ), ]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &matcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &matcher, + &CopyRecords::default(), + ); } // Diff while filtering by `path6` (directory1 -> file1+(directory1-absent)) as @@ -1161,7 +1315,12 @@ fn test_diff_dir_file() { .block_on(); let expected_diff = vec![(path6.to_owned(), (Merge::absent(), right_value(path6)))]; assert_eq!(actual_diff, expected_diff); - diff_stream_equals_iter(&left_merged, &right_merged, &matcher); + diff_stream_equals_iter( + &left_merged, + &right_merged, + &matcher, + &CopyRecords::default(), + ); } }