just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

merged_tree: make TreeDiffDirItem not self-referential

This removes another dependency on `ouroboros`, for a small
performance hit:


```
❯ hyperfine --warmup 3 --runs 30 \
'/tmp/jj-before --ignore-working-copy diff -s --from v5.0 --to v6.0' \
'/tmp/jj-after --ignore-working-copy diff -s --from v5.0 --to v6.0'
Benchmark 1: /tmp/jj-before --ignore-working-copy diff -s --from v5.0 --to v6.0
Time (mean ± σ): 689.7 ms ± 23.9 ms [User: 400.0 ms, System: 289.8 ms]
Range (min … max): 666.9 ms … 759.2 ms 30 runs

Benchmark 2: /tmp/jj-after --ignore-working-copy diff -s --from v5.0 --to v6.0
Time (mean ± σ): 710.9 ms ± 19.2 ms [User: 420.4 ms, System: 290.6 ms]
Range (min … max): 688.5 ms … 752.0 ms 30 runs

Summary
'/tmp/jj-before --ignore-working-copy diff -s --from v5.0 --to v6.0' ran
1.03 ± 0.05 times faster than '/tmp/jj-after --ignore-working-copy diff -s --from v5.0 --to v6.0'
```

authored by

Martin von Zweigbergk and committed by
Martin von Zweigbergk
e1a02c5c 61d87fe2

+86 -61
+86 -61
lib/src/merged_tree.rs
··· 803 803 matcher: &'matcher dyn Matcher, 804 804 } 805 805 806 - #[ouroboros::self_referencing] 807 806 struct TreeDiffDirItem { 808 - path: RepoPath, 809 807 tree1: MergedTree, 810 808 tree2: MergedTree, 811 - #[borrows(tree1, tree2)] 812 - #[not_covariant] 813 - entry_iterator: TreeEntryDiffIterator<'this>, 809 + entries: Vec<(RepoPath, MergedTreeValue, MergedTreeValue)>, 814 810 } 815 811 816 812 enum TreeDiffItem { ··· 829 825 let mut stack = Vec::new(); 830 826 if !matcher.visit(&root_dir).is_nothing() { 831 827 stack.push(TreeDiffItem::Dir(TreeDiffDirItem::from_trees( 832 - root_dir, tree1, tree2, 828 + &root_dir, tree1, tree2, matcher, 833 829 ))); 834 830 }; 835 831 Self { stack, matcher } ··· 871 867 } 872 868 873 869 impl TreeDiffDirItem { 874 - fn from_trees(path: RepoPath, tree1: MergedTree, tree2: MergedTree) -> Self { 875 - Self::new(path, tree1, tree2, |tree1, tree2| { 876 - TreeEntryDiffIterator::new(tree1, tree2) 877 - }) 870 + fn from_trees( 871 + dir: &RepoPath, 872 + tree1: MergedTree, 873 + tree2: MergedTree, 874 + matcher: &dyn Matcher, 875 + ) -> Self { 876 + let mut entries = vec![]; 877 + for (name, before, after) in TreeEntryDiffIterator::new(&tree1, &tree2) { 878 + let path = dir.join(name); 879 + let before = before.to_merge(); 880 + let after = after.to_merge(); 881 + let tree_before = before.is_tree(); 882 + let tree_after = after.is_tree(); 883 + // Check if trees and files match, but only if either side is a tree or a file 884 + // (don't query the matcher unnecessarily). 885 + let tree_matches = (tree_before || tree_after) && !matcher.visit(&path).is_nothing(); 886 + let file_matches = (!tree_before || !tree_after) && matcher.matches(&path); 887 + 888 + // Replace trees or files that don't match by `Merge::absent()` 889 + let before = if (tree_before && tree_matches) || (!tree_before && file_matches) { 890 + before 891 + } else { 892 + Merge::absent() 893 + }; 894 + let after = if (tree_after && tree_matches) || (!tree_after && file_matches) { 895 + after 896 + } else { 897 + Merge::absent() 898 + }; 899 + if before.is_absent() && after.is_absent() { 900 + continue; 901 + } 902 + entries.push((path, before, after)); 903 + } 904 + entries.reverse(); 905 + Self { 906 + tree1, 907 + tree2, 908 + entries, 909 + } 878 910 } 879 911 } 880 912 ··· 883 915 884 916 fn next(&mut self) -> Option<Self::Item> { 885 917 while let Some(top) = self.stack.last_mut() { 886 - let ((path, tree1, tree2), (name, before, after)) = match top { 887 - TreeDiffItem::Dir(dir) => { 888 - match dir.with_mut(|x| ((x.path, x.tree1, x.tree2), x.entry_iterator.next())) { 889 - (dir, Some((name, before, after))) => { 890 - (dir, (name, before.to_merge(), after.to_merge())) 891 - } 892 - _ => { 893 - self.stack.pop().unwrap(); 894 - continue; 895 - } 918 + let (dir, (path, before, after)) = match top { 919 + TreeDiffItem::Dir(dir) => match dir.entries.pop() { 920 + Some(entry) => (dir, entry), 921 + None => { 922 + self.stack.pop().unwrap(); 923 + continue; 896 924 } 897 - } 925 + }, 898 926 TreeDiffItem::File(..) => { 899 - if let TreeDiffItem::File(name, before, after) = self.stack.pop().unwrap() { 900 - return Some((name, Ok((before, after)))); 927 + if let TreeDiffItem::File(path, before, after) = self.stack.pop().unwrap() { 928 + return Some((path, Ok((before, after)))); 901 929 } else { 902 930 unreachable!(); 903 931 } 904 932 } 905 933 }; 906 934 907 - let path = path.join(name); 908 935 let tree_before = before.is_tree(); 909 936 let tree_after = after.is_tree(); 910 - let post_subdir = 911 - if (tree_before || tree_after) && !self.matcher.visit(&path).is_nothing() { 912 - let (before_tree, after_tree) = async { 913 - let before_tree = Self::tree(tree1, &path, &before); 914 - let after_tree = Self::tree(tree2, &path, &after); 915 - futures::join!(before_tree, after_tree) 937 + let post_subdir = if tree_before || tree_after { 938 + let (before_tree, after_tree) = async { 939 + let before_tree = Self::tree(&dir.tree1, &path, &before); 940 + let after_tree = Self::tree(&dir.tree2, &path, &after); 941 + futures::join!(before_tree, after_tree) 942 + } 943 + .block_on(); 944 + let before_tree = match before_tree { 945 + Ok(tree) => tree, 946 + Err(err) => { 947 + return Some((path, Err(err))); 916 948 } 917 - .block_on(); 918 - let before_tree = match before_tree { 919 - Ok(tree) => tree, 920 - Err(err) => { 921 - return Some((path, Err(err))); 922 - } 923 - }; 924 - let after_tree = match after_tree { 925 - Ok(tree) => tree, 926 - Err(err) => { 927 - return Some((path, Err(err))); 928 - } 929 - }; 930 - let subdir = TreeDiffDirItem::from_trees(path.clone(), before_tree, after_tree); 931 - self.stack.push(TreeDiffItem::Dir(subdir)); 932 - self.stack.len() - 1 933 - } else { 934 - self.stack.len() 935 949 }; 936 - if self.matcher.matches(&path) { 937 - if !tree_before && tree_after { 938 - if before.is_present() { 939 - return Some((path, Ok((before, Merge::absent())))); 940 - } 941 - } else if tree_before && !tree_after { 942 - if after.is_present() { 943 - self.stack.insert( 944 - post_subdir, 945 - TreeDiffItem::File(path, Merge::absent(), after), 946 - ); 950 + let after_tree = match after_tree { 951 + Ok(tree) => tree, 952 + Err(err) => { 953 + return Some((path, Err(err))); 947 954 } 948 - } else if !tree_before && !tree_after { 949 - return Some((path, Ok((before, after)))); 955 + }; 956 + let subdir = 957 + TreeDiffDirItem::from_trees(&path, before_tree, after_tree, self.matcher); 958 + self.stack.push(TreeDiffItem::Dir(subdir)); 959 + self.stack.len() - 1 960 + } else { 961 + self.stack.len() 962 + }; 963 + if !tree_before && tree_after { 964 + if before.is_present() { 965 + return Some((path, Ok((before, Merge::absent())))); 950 966 } 967 + } else if tree_before && !tree_after { 968 + if after.is_present() { 969 + self.stack.insert( 970 + post_subdir, 971 + TreeDiffItem::File(path, Merge::absent(), after), 972 + ); 973 + } 974 + } else if !tree_before && !tree_after { 975 + return Some((path, Ok((before, after)))); 951 976 } 952 977 } 953 978 None