ok/jj
1
0
Fork 0
forked from mirrors/jj

merged_tree: propagate backend errors in diff iterator

I want to fix error propagation before I start using async in this
code. This makes the diff iterator propagate errors from reading tree
objects.

Errors include the path and don't stop the iteration. The idea is that
we should be able to show the user an error inline in diff output if
we failed to read a tree. That's going to be especially useful for
backends that can return `BackendError::AccessDenied`. That error
variant doesn't yet exist, but I plan to add it, and use it in
Google's internal backend.
This commit is contained in:
Martin von Zweigbergk 2023-09-07 11:35:59 -07:00 committed by Martin von Zweigbergk
parent 309f1200d6
commit a1ef9dc845
10 changed files with 132 additions and 101 deletions

View file

@ -1567,7 +1567,8 @@ impl WorkspaceCommandTransaction<'_> {
Ok(right_tree.id().clone()) Ok(right_tree.id().clone())
} else { } else {
let mut tree_builder = MergedTreeBuilder::new(left_tree.id().clone()); let mut tree_builder = MergedTreeBuilder::new(left_tree.id().clone());
for (repo_path, _left, right) in left_tree.diff(right_tree, matcher) { for (repo_path, diff) in left_tree.diff(right_tree, matcher) {
let (_left, right) = diff?;
tree_builder.set_or_remove(repo_path, right); tree_builder.set_or_remove(repo_path, right);
} }
Ok(tree_builder.write_tree(self.repo().store())?) Ok(tree_builder.write_tree(self.repo().store())?)

View file

@ -3175,7 +3175,8 @@ fn cmd_restore(
let matcher = workspace_command.matcher_from_values(&args.paths)?; let matcher = workspace_command.matcher_from_values(&args.paths)?;
let mut tree_builder = MergedTreeBuilder::new(to_commit.tree_id().clone()); let mut tree_builder = MergedTreeBuilder::new(to_commit.tree_id().clone());
let to_tree = to_commit.tree()?; let to_tree = to_commit.tree()?;
for (repo_path, before, _after) in from_tree.diff(&to_tree, matcher.as_ref()) { for (repo_path, diff) in from_tree.diff(&to_tree, matcher.as_ref()) {
let (before, _after) = diff?;
tree_builder.set_or_remove(repo_path, before); tree_builder.set_or_remove(repo_path, before);
} }
tree_builder.write_tree(workspace_command.repo().store())? tree_builder.write_tree(workspace_command.repo().store())?

View file

@ -408,8 +408,9 @@ pub fn show_color_words_diff(
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let repo = workspace_command.repo(); let repo = workspace_command.repo();
formatter.push_label("diff")?; formatter.push_label("diff")?;
for (path, left_value, right_value) in tree_diff { for (path, diff) in tree_diff {
let ui_path = workspace_command.format_file_path(&path); let ui_path = workspace_command.format_file_path(&path);
let (left_value, right_value) = diff?;
if left_value.is_absent() { if left_value.is_absent() {
let right_content = diff_content(repo, &path, &right_value)?; let right_content = diff_content(repo, &path, &right_value)?;
let description = basic_diff_file_type(&right_value); let description = basic_diff_file_type(&right_value);
@ -686,8 +687,9 @@ pub fn show_git_diff(
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let repo = workspace_command.repo(); let repo = workspace_command.repo();
formatter.push_label("diff")?; formatter.push_label("diff")?;
for (path, left_value, right_value) in tree_diff { for (path, diff) in tree_diff {
let path_string = path.to_internal_file_string(); let path_string = path.to_internal_file_string();
let (left_value, right_value) = diff?;
if left_value.is_absent() { if left_value.is_absent() {
let right_part = git_diff_part(repo, &path, &right_value)?; let right_part = git_diff_part(repo, &path, &right_value)?;
formatter.with_label("file_header", |formatter| { formatter.with_label("file_header", |formatter| {
@ -746,7 +748,8 @@ pub fn show_diff_summary(
tree_diff: TreeDiffIterator, tree_diff: TreeDiffIterator,
) -> io::Result<()> { ) -> io::Result<()> {
formatter.with_label("diff", |formatter| { formatter.with_label("diff", |formatter| {
for (repo_path, before, after) in tree_diff { for (repo_path, diff) in tree_diff {
let (before, after) = diff.unwrap();
if before.is_present() && after.is_present() { if before.is_present() && after.is_present() {
writeln!( writeln!(
formatter.labeled("modified"), formatter.labeled("modified"),
@ -806,7 +809,8 @@ pub fn show_diff_stat(
let mut stats: Vec<DiffStat> = vec![]; let mut stats: Vec<DiffStat> = vec![];
let mut max_path_width = 0; let mut max_path_width = 0;
let mut max_diffs = 0; let mut max_diffs = 0;
for (repo_path, left, right) in tree_diff { for (repo_path, diff) in tree_diff {
let (left, right) = diff?;
let path = workspace_command.format_file_path(&repo_path); let path = workspace_command.format_file_path(&repo_path);
let left_content = diff_content(workspace_command.repo(), &repo_path, &left)?; let left_content = diff_content(workspace_command.repo(), &repo_path, &left)?;
let right_content = diff_content(workspace_command.repo(), &repo_path, &right)?; let right_content = diff_content(workspace_command.repo(), &repo_path, &right)?;
@ -873,7 +877,8 @@ pub fn show_types(
tree_diff: TreeDiffIterator, tree_diff: TreeDiffIterator,
) -> io::Result<()> { ) -> io::Result<()> {
formatter.with_label("diff", |formatter| { formatter.with_label("diff", |formatter| {
for (repo_path, before, after) in tree_diff { for (repo_path, diff) in tree_diff {
let (before, after) = diff.unwrap();
writeln!( writeln!(
formatter.labeled("modified"), formatter.labeled("modified"),
"{}{} {}", "{}{} {}",

View file

@ -408,10 +408,10 @@ pub fn edit_diff_builtin(
matcher: &dyn Matcher, matcher: &dyn Matcher,
) -> Result<MergedTreeId, BuiltinToolError> { ) -> Result<MergedTreeId, BuiltinToolError> {
let store = left_tree.store().clone(); let store = left_tree.store().clone();
let changed_files = left_tree let changed_files: Vec<_> = left_tree
.diff(right_tree, matcher) .diff(right_tree, matcher)
.map(|(path, _left, _right)| path) .map(|(path, diff)| diff.map(|_| path))
.collect_vec(); .try_collect()?;
let files = make_diff_files(&store, left_tree, right_tree, &changed_files)?; let files = make_diff_files(&store, left_tree, right_tree, &changed_files)?;
let recorder = scm_record::Recorder::new( let recorder = scm_record::Recorder::new(
scm_record::RecordState { scm_record::RecordState {

View file

@ -240,7 +240,7 @@ fn check_out_trees(
) -> Result<DiffWorkingCopies, DiffCheckoutError> { ) -> Result<DiffWorkingCopies, DiffCheckoutError> {
let changed_files = left_tree let changed_files = left_tree
.diff(right_tree, matcher) .diff(right_tree, matcher)
.map(|(path, _left, _right)| path) .map(|(path, _diff)| path)
.collect_vec(); .collect_vec();
let temp_dir = new_utf8_temp_dir("jj-diff-").map_err(DiffCheckoutError::SetUpDir)?; let temp_dir = new_utf8_temp_dir("jj-diff-").map_err(DiffCheckoutError::SetUpDir)?;

View file

@ -1152,7 +1152,8 @@ impl TreeState {
removed_files: 0, removed_files: 0,
skipped_files: 0, skipped_files: 0,
}; };
for (path, before, after) in old_tree.diff(new_tree, matcher) { for (path, diff) in old_tree.diff(new_tree, matcher) {
let (before, after) = diff?;
if after.is_absent() { if after.is_absent() {
stats.removed_files += 1; stats.removed_files += 1;
} else if before.is_absent() { } else if before.is_absent() {
@ -1226,7 +1227,8 @@ impl TreeState {
other => ResetError::InternalBackendError(other), other => ResetError::InternalBackendError(other),
})?; })?;
for (path, _before, after) in old_tree.diff(new_tree, self.sparse_matcher().as_ref()) { for (path, diff) in old_tree.diff(new_tree, self.sparse_matcher().as_ref()) {
let (_before, after) = diff?;
if after.is_absent() { if after.is_absent() {
self.file_states.remove(&path); self.file_states.remove(&path);
} else { } else {

View file

@ -22,6 +22,7 @@ use std::{iter, vec};
use futures::executor::block_on; use futures::executor::block_on;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use futures::TryStreamExt;
use itertools::Itertools; use itertools::Itertools;
use crate::backend::{BackendError, BackendResult, ConflictId, MergedTreeId, TreeId, TreeValue}; use crate::backend::{BackendError, BackendResult, ConflictId, MergedTreeId, TreeId, TreeValue};
@ -337,11 +338,16 @@ impl MergedTree {
/// Collects lists of modified, added, and removed files between this tree /// Collects lists of modified, added, and removed files between this tree
/// and another tree. /// and another tree.
pub fn diff_summary(&self, other: &MergedTree, matcher: &dyn Matcher) -> DiffSummary { pub fn diff_summary(
&self,
other: &MergedTree,
matcher: &dyn Matcher,
) -> BackendResult<DiffSummary> {
let mut modified = vec![]; let mut modified = vec![];
let mut added = vec![]; let mut added = vec![];
let mut removed = vec![]; let mut removed = vec![];
for (file, before, after) in self.diff(other, matcher) { for (file, diff) in self.diff(other, matcher) {
let (before, after) = diff?;
if before.is_absent() { if before.is_absent() {
added.push(file); added.push(file);
} else if after.is_absent() { } else if after.is_absent() {
@ -353,11 +359,11 @@ impl MergedTree {
modified.sort(); modified.sort();
added.sort(); added.sort();
removed.sort(); removed.sort();
DiffSummary { Ok(DiffSummary {
modified, modified,
added, added,
removed, removed,
} })
} }
/// Merges this tree with `other`, using `base` as base. /// Merges this tree with `other`, using `base` as base.
@ -814,20 +820,28 @@ impl<'matcher> TreeDiffIterator<'matcher> {
Self { stack, matcher } Self { stack, matcher }
} }
async fn single_tree(store: &Arc<Store>, dir: &RepoPath, value: Option<&TreeValue>) -> Tree { async fn single_tree(
store: &Arc<Store>,
dir: &RepoPath,
value: Option<&TreeValue>,
) -> BackendResult<Tree> {
match value { match value {
Some(TreeValue::Tree(tree_id)) => store.get_tree_async(dir, tree_id).await.unwrap(), Some(TreeValue::Tree(tree_id)) => store.get_tree_async(dir, tree_id).await,
_ => Tree::null(store.clone(), dir.clone()), _ => Ok(Tree::null(store.clone(), dir.clone())),
} }
} }
/// Gets the given tree if `value` is a tree, otherwise an empty tree. /// Gets the given tree if `value` is a tree, otherwise an empty tree.
async fn tree(tree: &MergedTree, dir: &RepoPath, values: &MergedTreeValue) -> MergedTree { async fn tree(
tree: &MergedTree,
dir: &RepoPath,
values: &MergedTreeValue,
) -> BackendResult<MergedTree> {
let trees = if values.is_tree() { let trees = if values.is_tree() {
let builder: MergeBuilder<Tree> = futures::stream::iter(values.iter()) let builder: MergeBuilder<Tree> = futures::stream::iter(values.iter())
.then(|value| Self::single_tree(tree.store(), dir, value.as_ref())) .then(|value| Self::single_tree(tree.store(), dir, value.as_ref()))
.collect() .try_collect()
.await; .await?;
builder.build() builder.build()
} else { } else {
Merge::resolved(Tree::null(tree.store().clone(), dir.clone())) Merge::resolved(Tree::null(tree.store().clone(), dir.clone()))
@ -835,8 +849,8 @@ impl<'matcher> TreeDiffIterator<'matcher> {
// Maintain the type of tree, so we resolve `TreeValue::Conflict` as necessary // Maintain the type of tree, so we resolve `TreeValue::Conflict` as necessary
// in the subtree // in the subtree
match tree { match tree {
MergedTree::Legacy(_) => MergedTree::Legacy(trees.into_resolved().unwrap()), MergedTree::Legacy(_) => Ok(MergedTree::Legacy(trees.into_resolved().unwrap())),
MergedTree::Merge(_) => MergedTree::Merge(trees), MergedTree::Merge(_) => Ok(MergedTree::Merge(trees)),
} }
} }
} }
@ -857,7 +871,7 @@ impl TreeDiffDirItem {
} }
impl Iterator for TreeDiffIterator<'_> { impl Iterator for TreeDiffIterator<'_> {
type Item = (RepoPath, MergedTreeValue, MergedTreeValue); type Item = (RepoPath, BackendResult<(MergedTreeValue, MergedTreeValue)>);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.last_mut() { while let Some(top) = self.stack.last_mut() {
@ -872,7 +886,7 @@ impl Iterator for TreeDiffIterator<'_> {
} }
TreeDiffItem::File(..) => { TreeDiffItem::File(..) => {
if let TreeDiffItem::File(name, before, after) = self.stack.pop().unwrap() { if let TreeDiffItem::File(name, before, after) = self.stack.pop().unwrap() {
return Some((name, before, after)); return Some((name, Ok((before, after))));
} else { } else {
unreachable!(); unreachable!();
} }
@ -889,6 +903,18 @@ impl Iterator for TreeDiffIterator<'_> {
let after_tree = Self::tree(dir.tree2.as_ref(), &path, &after); let after_tree = Self::tree(dir.tree2.as_ref(), &path, &after);
futures::join!(before_tree, after_tree) futures::join!(before_tree, after_tree)
}); });
let before_tree = match before_tree {
Ok(tree) => tree,
Err(err) => {
return Some((path, Err(err)));
}
};
let after_tree = match after_tree {
Ok(tree) => tree,
Err(err) => {
return Some((path, Err(err)));
}
};
let subdir = TreeDiffDirItem::new(path.clone(), before_tree, after_tree); let subdir = TreeDiffDirItem::new(path.clone(), before_tree, after_tree);
self.stack.push(TreeDiffItem::Dir(subdir)); self.stack.push(TreeDiffItem::Dir(subdir));
self.stack.len() - 1 self.stack.len() - 1
@ -898,7 +924,7 @@ impl Iterator for TreeDiffIterator<'_> {
if self.matcher.matches(&path) { if self.matcher.matches(&path) {
if !tree_before && tree_after { if !tree_before && tree_after {
if before.is_present() { if before.is_present() {
return Some((path, before, Merge::absent())); return Some((path, Ok((before, Merge::absent()))));
} }
} else if tree_before && !tree_after { } else if tree_before && !tree_after {
if after.is_present() { if after.is_present() {
@ -908,7 +934,7 @@ impl Iterator for TreeDiffIterator<'_> {
); );
} }
} else if !tree_before && !tree_after { } else if !tree_before && !tree_after {
return Some((path, before, after)); return Some((path, Ok((before, after))));
} }
} }
} }

View file

@ -83,7 +83,8 @@ fn test_initial(backend: TestRepoBackend) {
.root_commit() .root_commit()
.tree() .tree()
.unwrap() .unwrap()
.diff_summary(&commit.tree().unwrap(), &EverythingMatcher), .diff_summary(&commit.tree().unwrap(), &EverythingMatcher)
.unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![dir_file_path, root_file_path], added: vec![dir_file_path, root_file_path],
@ -167,7 +168,8 @@ fn test_rewrite(backend: TestRepoBackend) {
.root_commit() .root_commit()
.tree() .tree()
.unwrap() .unwrap()
.diff_summary(&rewritten_commit.tree().unwrap(), &EverythingMatcher), .diff_summary(&rewritten_commit.tree().unwrap(), &EverythingMatcher)
.unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![dir_file_path.clone(), root_file_path], added: vec![dir_file_path.clone(), root_file_path],
@ -178,7 +180,8 @@ fn test_rewrite(backend: TestRepoBackend) {
initial_commit initial_commit
.tree() .tree()
.unwrap() .unwrap()
.diff_summary(&rewritten_commit.tree().unwrap(), &EverythingMatcher), .diff_summary(&rewritten_commit.tree().unwrap(), &EverythingMatcher)
.unwrap(),
DiffSummary { DiffSummary {
modified: vec![dir_file_path], modified: vec![dir_file_path],
added: vec![], added: vec![],

View file

@ -46,7 +46,7 @@ fn test_types() {
); );
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &EverythingMatcher), tree1.diff_summary(&tree2, &EverythingMatcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![modified_path], modified: vec![modified_path],
added: vec![added_path], added: vec![added_path],
@ -67,7 +67,7 @@ fn test_tree_file_transition() {
let tree2 = create_tree(repo, &[(&dir_path, "contents")]); let tree2 = create_tree(repo, &[(&dir_path, "contents")]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &EverythingMatcher), tree1.diff_summary(&tree2, &EverythingMatcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![dir_path.clone()], added: vec![dir_path.clone()],
@ -75,7 +75,7 @@ fn test_tree_file_transition() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &EverythingMatcher), tree2.diff_summary(&tree1, &EverythingMatcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![dir_file_path], added: vec![dir_file_path],
@ -124,7 +124,7 @@ fn test_sorting() {
); );
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &EverythingMatcher), tree1.diff_summary(&tree2, &EverythingMatcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![a_path.clone(), f_a_path.clone(), f_f_a_path.clone()], modified: vec![a_path.clone(), f_a_path.clone(), f_f_a_path.clone()],
added: vec![ added: vec![
@ -139,7 +139,7 @@ fn test_sorting() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &EverythingMatcher), tree2.diff_summary(&tree1, &EverythingMatcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![a_path, f_a_path, f_f_a_path], modified: vec![a_path, f_a_path, f_f_a_path],
added: vec![], added: vec![],
@ -161,7 +161,7 @@ fn test_matcher_dir_file_transition() {
let matcher = FilesMatcher::new(&[a_path.clone()]); let matcher = FilesMatcher::new(&[a_path.clone()]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &matcher), tree1.diff_summary(&tree2, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![], added: vec![],
@ -169,7 +169,7 @@ fn test_matcher_dir_file_transition() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &matcher), tree2.diff_summary(&tree1, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![a_path.clone()], added: vec![a_path.clone()],
@ -179,7 +179,7 @@ fn test_matcher_dir_file_transition() {
let matcher = FilesMatcher::new(&[a_a_path.clone()]); let matcher = FilesMatcher::new(&[a_a_path.clone()]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &matcher), tree1.diff_summary(&tree2, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![a_a_path.clone()], added: vec![a_a_path.clone()],
@ -187,7 +187,7 @@ fn test_matcher_dir_file_transition() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &matcher), tree2.diff_summary(&tree1, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![], added: vec![],
@ -197,7 +197,7 @@ fn test_matcher_dir_file_transition() {
let matcher = FilesMatcher::new(&[a_path.clone(), a_a_path.clone()]); let matcher = FilesMatcher::new(&[a_path.clone(), a_a_path.clone()]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &matcher), tree1.diff_summary(&tree2, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![a_a_path.clone()], added: vec![a_a_path.clone()],
@ -205,7 +205,7 @@ fn test_matcher_dir_file_transition() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &matcher), tree2.diff_summary(&tree1, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![], modified: vec![],
added: vec![a_path], added: vec![a_path],
@ -241,7 +241,7 @@ fn test_matcher_normal_cases() {
let matcher = FilesMatcher::new(&[a_path.clone(), z_path.clone()]); let matcher = FilesMatcher::new(&[a_path.clone(), z_path.clone()]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &matcher), tree1.diff_summary(&tree2, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![a_path.clone()], modified: vec![a_path.clone()],
added: vec![z_path.clone()], added: vec![z_path.clone()],
@ -249,7 +249,7 @@ fn test_matcher_normal_cases() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &matcher), tree2.diff_summary(&tree1, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![a_path], modified: vec![a_path],
added: vec![], added: vec![],
@ -259,7 +259,7 @@ fn test_matcher_normal_cases() {
let matcher = FilesMatcher::new(&[dir1_a_path.clone(), dir2_b_path.clone()]); let matcher = FilesMatcher::new(&[dir1_a_path.clone(), dir2_b_path.clone()]);
assert_eq!( assert_eq!(
tree1.diff_summary(&tree2, &matcher), tree1.diff_summary(&tree2, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![dir1_a_path.clone()], modified: vec![dir1_a_path.clone()],
added: vec![dir2_b_path.clone()], added: vec![dir2_b_path.clone()],
@ -267,7 +267,7 @@ fn test_matcher_normal_cases() {
} }
); );
assert_eq!( assert_eq!(
tree2.diff_summary(&tree1, &matcher), tree2.diff_summary(&tree1, &matcher).unwrap(),
DiffSummary { DiffSummary {
modified: vec![dir1_a_path], modified: vec![dir1_a_path],
added: vec![], added: vec![],

View file

@ -681,30 +681,37 @@ fn test_diff_resolved() {
let diff = before_merged let diff = before_merged
.diff(&after_merged, &EverythingMatcher) .diff(&after_merged, &EverythingMatcher)
.map(|(path, diff)| (path, diff.unwrap()))
.collect_vec(); .collect_vec();
assert_eq!(diff.len(), 3); assert_eq!(diff.len(), 3);
assert_eq!( assert_eq!(
diff[0].clone(), diff[0].clone(),
( (
modified_path.clone(), modified_path.clone(),
Merge::resolved(before.path_value(&modified_path)), (
Merge::resolved(after.path_value(&modified_path)), Merge::resolved(before.path_value(&modified_path)),
Merge::resolved(after.path_value(&modified_path))
),
) )
); );
assert_eq!( assert_eq!(
diff[1].clone(), diff[1].clone(),
( (
removed_path.clone(), removed_path.clone(),
Merge::resolved(before.path_value(&removed_path)), (
Merge::absent(), Merge::resolved(before.path_value(&removed_path)),
Merge::absent()
),
) )
); );
assert_eq!( assert_eq!(
diff[2].clone(), diff[2].clone(),
( (
added_path.clone(), added_path.clone(),
Merge::absent(), (
Merge::resolved(after.path_value(&added_path)), Merge::absent(),
Merge::resolved(after.path_value(&added_path))
),
) )
); );
} }
@ -786,14 +793,14 @@ fn test_diff_conflicted() {
// Test the forwards diff // Test the forwards diff
let actual_diff = left_merged let actual_diff = left_merged
.diff(&right_merged, &EverythingMatcher) .diff(&right_merged, &EverythingMatcher)
.map(|(path, diff)| (path, diff.unwrap()))
.collect_vec(); .collect_vec();
let expected_diff = [&path2, &path3, &path4] let expected_diff = [&path2, &path3, &path4]
.iter() .iter()
.map(|path| { .map(|path| {
( (
(*path).clone(), (*path).clone(),
left_merged.path_value(path), (left_merged.path_value(path), right_merged.path_value(path)),
right_merged.path_value(path),
) )
}) })
.collect_vec(); .collect_vec();
@ -801,14 +808,14 @@ fn test_diff_conflicted() {
// Test the reverse diff // Test the reverse diff
let actual_diff = right_merged let actual_diff = right_merged
.diff(&left_merged, &EverythingMatcher) .diff(&left_merged, &EverythingMatcher)
.map(|(path, diff)| (path, diff.unwrap()))
.collect_vec(); .collect_vec();
let expected_diff = [&path2, &path3, &path4] let expected_diff = [&path2, &path3, &path4]
.iter() .iter()
.map(|path| { .map(|path| {
( (
(*path).clone(), (*path).clone(),
right_merged.path_value(path), (right_merged.path_value(path), left_merged.path_value(path)),
left_merged.path_value(path),
) )
}) })
.collect_vec(); .collect_vec();
@ -911,136 +918,122 @@ fn test_diff_dir_file() {
// Test the forwards diff // Test the forwards diff
let actual_diff = left_merged let actual_diff = left_merged
.diff(&right_merged, &EverythingMatcher) .diff(&right_merged, &EverythingMatcher)
.map(|(path, diff)| (path, diff.unwrap()))
.collect_vec(); .collect_vec();
let expected_diff = vec![ let expected_diff = vec![
// path1: file1 -> directory1 // path1: file1 -> directory1
( (
path1.clone(), path1.clone(),
left_merged.path_value(&path1), (left_merged.path_value(&path1), Merge::absent()),
Merge::absent(),
), ),
( (
path1.join(&file), path1.join(&file),
Merge::absent(), (Merge::absent(), right_merged.path_value(&path1.join(&file))),
right_merged.path_value(&path1.join(&file)),
), ),
// path2: file1 -> directory1+(directory2-absent) // path2: file1 -> directory1+(directory2-absent)
( (
path2.clone(), path2.clone(),
left_merged.path_value(&path2), (left_merged.path_value(&path2), Merge::absent()),
Merge::absent(),
), ),
( (
path2.join(&file), path2.join(&file),
Merge::absent(), (Merge::absent(), right_merged.path_value(&path2.join(&file))),
right_merged.path_value(&path2.join(&file)),
), ),
// path3: file1 -> directory1+(file1-absent) // path3: file1 -> directory1+(file1-absent)
( (
path3.clone(), path3.clone(),
left_merged.path_value(&path3), (
right_merged.path_value(&path3), left_merged.path_value(&path3),
right_merged.path_value(&path3),
),
), ),
// path4: file1+(file2-file3) -> directory1+(directory2-directory3) // path4: file1+(file2-file3) -> directory1+(directory2-directory3)
( (
path4.clone(), path4.clone(),
left_merged.path_value(&path4), (left_merged.path_value(&path4), Merge::absent()),
Merge::absent(),
), ),
( (
path4.join(&file), path4.join(&file),
Merge::absent(), (Merge::absent(), right_merged.path_value(&path4.join(&file))),
right_merged.path_value(&path4.join(&file)),
), ),
// path5: directory1 -> file1+(file2-absent) // path5: directory1 -> file1+(file2-absent)
( (
path5.join(&file), path5.join(&file),
left_merged.path_value(&path5.join(&file)), (left_merged.path_value(&path5.join(&file)), Merge::absent()),
Merge::absent(),
), ),
( (
path5.clone(), path5.clone(),
Merge::absent(), (Merge::absent(), right_merged.path_value(&path5)),
right_merged.path_value(&path5),
), ),
// path6: directory1 -> file1+(directory1-absent) // path6: directory1 -> file1+(directory1-absent)
( (
path6.join(&file), path6.join(&file),
left_merged.path_value(&path6.join(&file)), (left_merged.path_value(&path6.join(&file)), Merge::absent()),
Merge::absent(),
), ),
( (
path6.clone(), path6.clone(),
Merge::absent(), (Merge::absent(), right_merged.path_value(&path6)),
right_merged.path_value(&path6),
), ),
]; ];
assert_eq!(actual_diff, expected_diff); assert_eq!(actual_diff, expected_diff);
// Test the reverse diff // Test the reverse diff
let actual_diff = right_merged let actual_diff = right_merged
.diff(&left_merged, &EverythingMatcher) .diff(&left_merged, &EverythingMatcher)
.map(|(path, diff)| (path, diff.unwrap()))
.collect_vec(); .collect_vec();
let expected_diff = vec![ let expected_diff = vec![
// path1: file1 -> directory1 // path1: file1 -> directory1
( (
path1.join(&file), path1.join(&file),
right_merged.path_value(&path1.join(&file)), (right_merged.path_value(&path1.join(&file)), Merge::absent()),
Merge::absent(),
), ),
( (
path1.clone(), path1.clone(),
Merge::absent(), (Merge::absent(), left_merged.path_value(&path1)),
left_merged.path_value(&path1),
), ),
// path2: file1 -> directory1+(directory2-absent) // path2: file1 -> directory1+(directory2-absent)
( (
path2.join(&file), path2.join(&file),
right_merged.path_value(&path2.join(&file)), (right_merged.path_value(&path2.join(&file)), Merge::absent()),
Merge::absent(),
), ),
( (
path2.clone(), path2.clone(),
Merge::absent(), (Merge::absent(), left_merged.path_value(&path2)),
left_merged.path_value(&path2),
), ),
// path3: file1 -> directory1+(file1-absent) // path3: file1 -> directory1+(file1-absent)
( (
path3.clone(), path3.clone(),
right_merged.path_value(&path3), (
left_merged.path_value(&path3), right_merged.path_value(&path3),
left_merged.path_value(&path3),
),
), ),
// path4: file1+(file2-file3) -> directory1+(directory2-directory3) // path4: file1+(file2-file3) -> directory1+(directory2-directory3)
( (
path4.join(&file), path4.join(&file),
right_merged.path_value(&path4.join(&file)), (right_merged.path_value(&path4.join(&file)), Merge::absent()),
Merge::absent(),
), ),
( (
path4.clone(), path4.clone(),
Merge::absent(), (Merge::absent(), left_merged.path_value(&path4)),
left_merged.path_value(&path4),
), ),
// path5: directory1 -> file1+(file2-absent) // path5: directory1 -> file1+(file2-absent)
( (
path5.clone(), path5.clone(),
right_merged.path_value(&path5), (right_merged.path_value(&path5), Merge::absent()),
Merge::absent(),
), ),
( (
path5.join(&file), path5.join(&file),
Merge::absent(), (Merge::absent(), left_merged.path_value(&path5.join(&file))),
left_merged.path_value(&path5.join(&file)),
), ),
// path6: directory1 -> file1+(directory1-absent) // path6: directory1 -> file1+(directory1-absent)
( (
path6.clone(), path6.clone(),
right_merged.path_value(&path6), (right_merged.path_value(&path6), Merge::absent()),
Merge::absent(),
), ),
( (
path6.join(&file), path6.join(&file),
Merge::absent(), (Merge::absent(), left_merged.path_value(&path6.join(&file))),
left_merged.path_value(&path6.join(&file)),
), ),
]; ];
assert_eq!(actual_diff, expected_diff); assert_eq!(actual_diff, expected_diff);