diff --git a/lib/src/merge.rs b/lib/src/merge.rs index cd4926362c..84de55e0e9 100644 --- a/lib/src/merge.rs +++ b/lib/src/merge.rs @@ -689,6 +689,70 @@ fn describe_conflict_term(value: &TreeValue) -> String { } } +// TODO(ilyagr): DiffOfMerges might not need to be public, might need renaming + +/// A sequence of diffs forming a "conflicted diff" +/// +/// Conceptually, this is very similar to a merge, except it has the same number +/// of positive terms (corresponding to "adds" in a merge) and negative terms +/// (corresponding to "removes" in a merge). +/// +/// This can represent a diff of two merges, with corresponding terms cancelling +/// out. The adds of the negative (left-hand side) merge will then become a +/// negative term of the DiffOfMerges. However, in this case, it is not +/// remembered which term comes from which merge. By itself, a sequence of diffs +/// resulting from a diff of two merges cannot tell the difference between the +/// a) right-hand merge adding a diff (X-Y) to the left-hand side, or b) the +/// right-hand merge subtracting the opposite diff (Y-X) that was present on the +/// left-hand side. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct DiffOfMerges { + /// Alternates between positive and negative terms, starting with positive. + values: Vec, +} +impl DiffOfMerges { + /// Creates a `DiffOfMerges` from the given values, in which positive and + /// negative terms alternate. + pub fn from_vec(values: impl Into>) -> Self { + let values = values.into(); + assert!( + values.len() & 1 == 0, + "must have equal numbers of adds and removes" + ); + DiffOfMerges { values } + } + + /// Creates a new merge object from the given removes and adds. + // TODO: Rename to `from_negatives_positives` (?) + pub fn from_removes_adds( + removes: impl IntoIterator, + adds: impl IntoIterator, + ) -> Self { + let removes = removes.into_iter(); + let adds = adds.into_iter(); + let mut values = Vec::with_capacity(removes.size_hint().0 * 2); + for diff in removes.zip_longest(adds) { + let (remove, add) = diff + .both() + .expect("must have the same number of adds and removes"); + values.extend([remove, add]); + } + DiffOfMerges { values } + } + + /// Simplify the diff by removing equal positive and negative terms. + /// + /// The positive terms are allowed to be reordered freely, as are the + /// negative terms. The pairing of them into diffs is **not** preserved. + pub fn simplify(mut self) -> Self + where + T: PartialEq + Clone, + { + self.values = simplify_internal(self.values); + self + } +} + #[cfg(test)] mod tests { use super::*; @@ -697,6 +761,10 @@ mod tests { Merge::from_removes_adds(removes.to_vec(), adds.to_vec()) } + fn d(removes: &[T], adds: &[T]) -> DiffOfMerges { + DiffOfMerges::from_removes_adds(removes.to_vec(), adds.to_vec()) + } + #[test] fn test_trivial_merge() { assert_eq!(trivial_merge(&[], &[0]), Some(&0)); @@ -832,7 +900,7 @@ mod tests { assert_eq!(get_simplified_mapping(&[1, 0, 0, 0, 0]), vec![0]); assert_eq!(get_simplified_mapping(&[1, 0, 0, 0, 1]), vec![0, 3, 4],); assert_eq!(get_simplified_mapping(&[1, 0, 0, 0, 2]), vec![0, 3, 4],); - assert_eq!(get_simplified_mapping(&[1, 0, 1, 0, 0]), vec![0, 3, 2],); + assert_eq!(get_simplified_mapping(&[1, 0, 1, 0, 0]), vec![2, 3, 0],); assert_eq!( get_simplified_mapping(&[1, 0, 1, 0, 1]), vec![0, 1, 2, 3, 4], @@ -841,7 +909,7 @@ mod tests { get_simplified_mapping(&[1, 0, 1, 0, 2]), vec![0, 1, 2, 3, 4], ); - assert_eq!(get_simplified_mapping(&[1, 0, 2, 0, 0]), vec![0, 3, 2],); + assert_eq!(get_simplified_mapping(&[1, 0, 2, 0, 0]), vec![2, 3, 0],); assert_eq!( get_simplified_mapping(&[1, 0, 2, 0, 1]), vec![0, 1, 2, 3, 4], @@ -864,16 +932,16 @@ mod tests { assert_eq!(get_simplified_mapping(&[0, 0, 2, 1, 1]), vec![2]); assert_eq!(get_simplified_mapping(&[0, 0, 2, 1, 2]), vec![2, 3, 4],); assert_eq!(get_simplified_mapping(&[0, 0, 2, 1, 3]), vec![2, 3, 4],); - assert_eq!(get_simplified_mapping(&[1, 0, 0, 1, 0]), vec![2]); + assert_eq!(get_simplified_mapping(&[1, 0, 0, 1, 0]), vec![4]); assert_eq!(get_simplified_mapping(&[1, 0, 0, 1, 1]), vec![4]); assert_eq!(get_simplified_mapping(&[1, 0, 0, 1, 2]), vec![4]); assert_eq!(get_simplified_mapping(&[1, 0, 1, 1, 0]), vec![2]); - assert_eq!(get_simplified_mapping(&[1, 0, 1, 1, 1]), vec![4, 1, 2],); - assert_eq!(get_simplified_mapping(&[1, 0, 1, 1, 2]), vec![4, 1, 2],); + assert_eq!(get_simplified_mapping(&[1, 0, 1, 1, 1]), vec![2, 1, 4],); + assert_eq!(get_simplified_mapping(&[1, 0, 1, 1, 2]), vec![2, 1, 4],); assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 0]), vec![2]); - assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 1]), vec![4, 1, 2],); - assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 2]), vec![4, 1, 2],); - assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 3]), vec![4, 1, 2],); + assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 1]), vec![2, 1, 4],); + assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 2]), vec![2, 1, 4],); + assert_eq!(get_simplified_mapping(&[1, 0, 2, 1, 3]), vec![2, 1, 4],); assert_eq!(get_simplified_mapping(&[2, 0, 0, 1, 0]), vec![0, 3, 4],); assert_eq!(get_simplified_mapping(&[2, 0, 0, 1, 1]), vec![0]); assert_eq!(get_simplified_mapping(&[2, 0, 0, 1, 2]), vec![0, 3, 4],); @@ -882,7 +950,7 @@ mod tests { assert_eq!(get_simplified_mapping(&[2, 0, 1, 1, 1]), vec![0, 1, 4],); assert_eq!(get_simplified_mapping(&[2, 0, 1, 1, 2]), vec![0, 1, 4],); assert_eq!(get_simplified_mapping(&[2, 0, 1, 1, 3]), vec![0, 1, 4],); - assert_eq!(get_simplified_mapping(&[2, 0, 2, 1, 0]), vec![0, 3, 2],); + assert_eq!(get_simplified_mapping(&[2, 0, 2, 1, 0]), vec![2, 3, 0],); assert_eq!(get_simplified_mapping(&[2, 0, 2, 1, 1]), vec![0, 1, 2],); assert_eq!( get_simplified_mapping(&[2, 0, 2, 1, 2]), @@ -892,7 +960,7 @@ mod tests { get_simplified_mapping(&[2, 0, 2, 1, 3]), vec![0, 1, 2, 3, 4], ); - assert_eq!(get_simplified_mapping(&[2, 0, 3, 1, 0]), vec![0, 3, 2],); + assert_eq!(get_simplified_mapping(&[2, 0, 3, 1, 0]), vec![2, 3, 0],); assert_eq!(get_simplified_mapping(&[2, 0, 3, 1, 1]), vec![0, 1, 2],); assert_eq!( get_simplified_mapping(&[2, 0, 3, 1, 2]), @@ -908,12 +976,12 @@ mod tests { ); assert_eq!( get_simplified_mapping(&[3, 0, 4, 1, 5, 2, 0]), - vec![0, 3, 4, 5, 2], + vec![2, 3, 4, 5, 0], ); } #[test] - fn test_simplify() { + fn test_simplify_merge() { // 1-way merge assert_eq!(c(&[], &[0]).simplify(), c(&[], &[0])); // 3-way merge @@ -981,6 +1049,19 @@ mod tests { ); } + #[test] + fn test_simplify_diff_of_merges() { + assert_eq!(d::(&[], &[]).simplify(), d(&[], &[])); + assert_eq!(d(&[0], &[0]).simplify(), d(&[], &[])); + assert_eq!(d(&[1], &[0]).simplify(), d(&[1], &[0])); + assert_eq!(d(&[0, 0], &[0, 0]).simplify(), d(&[], &[])); + assert_eq!(d(&[0, 1], &[1, 0]).simplify(), d(&[], &[])); + assert_eq!(d(&[0, 1], &[0, 1]).simplify(), d(&[], &[])); + assert_eq!(d(&[1, 1], &[0, 1]).simplify(), d(&[1], &[0])); + assert_eq!(d(&[1, 0], &[0, 2]).simplify(), d(&[1], &[2])); + assert_eq!(d(&[1, 0], &[3, 2]).simplify(), d(&[1, 0], &[3, 2])); + } + #[test] fn test_update_from_simplified() { // 1-way merge