public void ConflictFirstElement() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod1List = new List <string>() { "X", "B", "C", "D", "E", "F" }; var mod2List = new List <string>() { "Y", "B", "C", "D", "E", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(2, diff3.Count); // Conflict on base:"A", v1:"X", v2:"Y" Assert.AreEqual(Diff3ChangeType.Conflict, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); // First set is identical Assert.AreEqual(Diff3ChangeType.Equal, diff3[1].ChangeType); Assert.AreEqual(new Span(1, 5), diff3[1].Base); Assert.AreEqual(new Span(1, 5), diff3[1].From1); Assert.AreEqual(new Span(1, 5), diff3[1].From2); }
public void TestAllEquals() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod1List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod2List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(1, diff3.Count); // All elements are identical Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 5), diff3[0].Base); Assert.AreEqual(new Span(0, 5), diff3[0].From1); Assert.AreEqual(new Span(0, 5), diff3[0].From2); }
public void MergeFirstElementFrom1And2() { // 0 1 2 3 4 5 var baseList = new List <string>() { "X", "B", "C", "D", "E", "F" }; var mod1List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod2List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(2, diff3.Count); // Merge from v1 "X" to base, v2 Assert.AreEqual(Diff3ChangeType.MergeFrom1And2, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); // First set is identical Assert.AreEqual(Diff3ChangeType.Equal, diff3[1].ChangeType); Assert.AreEqual(new Span(1, 5), diff3[1].Base); Assert.AreEqual(new Span(1, 5), diff3[1].From1); Assert.AreEqual(new Span(1, 5), diff3[1].From2); }
public void CompareInsertsTest() { var original = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { } ".WrapWithMethod().Parse(); var changed1 = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { total = i + total; } ".WrapWithMethod().Parse(); var changed2 = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { total += i; } ".WrapWithMethod().Parse(); var conflicts = Diff3.Compare(original, changed1, changed2); Assert.IsTrue(conflicts.Conflicts.Any()); var strrep = conflicts.Conflicts.First().ToString(); }
public void CompareOverlapingTest() { var original = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { total = i + total; } ".WrapWithMethod().Parse(); var changed1 = @" var total = 0; ".WrapWithMethod().Parse(); //Minus 153 chars var changed2 = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { total += i; } ".WrapWithMethod().Parse(); var conflicts = Diff3.Compare(original, changed1, changed2); var localDiff = Diff.VisualDiff(conflicts.Local, conflicts.AncestorTree); var remoteDiff = Diff.VisualDiff(conflicts.Remote, conflicts.AncestorTree); conflicts.Conflicts = conflicts.Conflicts.ToList(); Assert.IsTrue(conflicts.Conflicts.Any()); var strrep = conflicts.Conflicts.First().ToString(); }
public void CompareNoConflictTest() { var original = @" var total = 0; var n = 100; for (var i = n - 1; i >= 0; i--) { total = i + total; } ".WrapWithMethod().Parse(); var changed1 = @" var total = 0; var n = 100; for (var i = 0; i < n; i++) { total = i + total; } ".WrapWithMethod().Parse(); var changed2 = @" var total = 0; var n = 100; for (var i = n - 1; i >= 0; i--) { total += i; } ".WrapWithMethod().Parse(); var conflicts = Diff3.Compare(original, changed1, changed2); var localDiff = Diff.VisualDiff(conflicts.Local, conflicts.AncestorTree); var remoteDiff = Diff.VisualDiff(conflicts.Remote, conflicts.AncestorTree); conflicts.Conflicts = conflicts.Conflicts.ToList(); Assert.IsTrue(!conflicts.Conflicts.Any()); }
public void MergeRemoveFrom2Middle() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C", "D" }; var mod1List = new List <string>() { "A", "B", "C", "D" }; var mod2List = new List <string>() { "A", "D" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(3, diff3.Count); Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); Assert.AreEqual(Diff3ChangeType.MergeFrom2, diff3[1].ChangeType); Assert.AreEqual(new Span(1, 2), diff3[1].Base); Assert.AreEqual(new Span(1, 2), diff3[1].From1); Assert.AreEqual(new Span(0, -1), diff3[1].From2); Assert.AreEqual(Diff3ChangeType.Equal, diff3[2].ChangeType); Assert.AreEqual(new Span(3, 3), diff3[2].Base); Assert.AreEqual(new Span(3, 3), diff3[2].From1); Assert.AreEqual(new Span(1, 1), diff3[2].From2); }
public void CompareFirstDiffLongSecondShortRegressionTest() { var original = @" int outval = 0; switch (0x5) { case 0x1: outval = 0x7; break; case 0x2: outval = 0x6; break; case 0x3: outval = 0x5; break; case 0x4: outval = 0x4; break; case 0x5: outval = 0x3; break; case 0x6: outval = 0x2; break; case 0x7: outval = 0x1; break; case 0x8: outval = 0x0; break; } ".WrapWithMethod().Parse(); var changed1 = @" int outval = 0x3; ".WrapWithMethod().Parse(); var changed2 = @" int outval = 0; switch (0x4) { case 0x4: outval = 0x4; break; } ".WrapWithMethod().Parse(); var conflicts = Diff3.Compare(original, changed1, changed2); var localDiff = Diff.VisualDiff(conflicts.Local, conflicts.AncestorTree); var remoteDiff = Diff.VisualDiff(conflicts.Remote, conflicts.AncestorTree); conflicts.Conflicts = conflicts.Conflicts.ToList(); Assert.IsTrue(conflicts.Conflicts.Any()); var strrep = conflicts.Conflicts.First().ToString(); }
public void ConflictAndMergeSimple() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod1List = new List <string>() { "A", "D", "E", "B", "C", "F" }; var mod2List = new List <string>() { "A", "B", "D", "E", "C", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); // 0 1 2 3 4 //base A B C,D,E F //v1 A D,E B C F //v2 A B D,E,C F Assert.AreEqual(5, diff3.Count); // Check 0) A same for base, v1, v2 Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); // Check 1) Merge from 1 Assert.AreEqual(Diff3ChangeType.MergeFrom1, diff3[1].ChangeType); Assert.AreEqual(Span.Invalid, diff3[1].Base); Assert.AreEqual(new Span(1, 2), diff3[1].From1); Assert.AreEqual(Span.Invalid, diff3[1].From2); // Check 2) B same for base, v1, v2 Assert.AreEqual(Diff3ChangeType.Equal, diff3[2].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[2].Base); Assert.AreEqual(new Span(3, 3), diff3[2].From1); Assert.AreEqual(new Span(1, 1), diff3[2].From2); // Check 3) Merge from base Assert.AreEqual(Diff3ChangeType.Conflict, diff3[3].ChangeType); Assert.AreEqual(new Span(2, 4), diff3[3].Base); Assert.AreEqual(new Span(4, 4), diff3[3].From1); Assert.AreEqual(new Span(2, 4), diff3[3].From2); // Check 4) B same for base, v1, v2 Assert.AreEqual(Diff3ChangeType.Equal, diff3[4].ChangeType); Assert.AreEqual(new Span(5, 5), diff3[4].Base); Assert.AreEqual(new Span(5, 5), diff3[4].From1); Assert.AreEqual(new Span(5, 5), diff3[4].From2); }
public void ConflictMultipleElementsFrom1And2() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "F" }; var mod1List = new List <string>() { "A", "X", "Y", "F" }; var mod2List = new List <string>() { "A", "Y", "Z", "W", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(5, diff3.Count); // 0) Equal A = A = A Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); // 1) Conflict on X Assert.AreEqual(Diff3ChangeType.Conflict, diff3[1].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[1].Base); Assert.AreEqual(new Span(1, 1), diff3[1].From1); Assert.AreEqual(Span.Invalid, diff3[1].From2); // 2) MergeFrom1And2 "Y" and "Y" Assert.AreEqual(Diff3ChangeType.MergeFrom1And2, diff3[2].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[2].Base); Assert.AreEqual(new Span(2, 2), diff3[2].From1); Assert.AreEqual(new Span(1, 1), diff3[2].From2); // 3) MergeFrom2 "Z" and "W" Assert.AreEqual(Diff3ChangeType.MergeFrom2, diff3[3].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[3].Base); Assert.AreEqual(Span.Invalid, diff3[3].From1); Assert.AreEqual(new Span(2, 3), diff3[3].From2); // 4) Equal "F" Assert.AreEqual(Diff3ChangeType.Equal, diff3[4].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[4].Base); Assert.AreEqual(new Span(3, 3), diff3[4].From1); Assert.AreEqual(new Span(4, 4), diff3[4].From2); }
public void CompareOutOfRangeRegressionTest() { var ancestor = File.ReadAllText("Newtonsoft.Ancestor.txt").WrapWithNamespace().Parse(); var local = File.ReadAllText("Newtonsoft.Local.txt").WrapWithNamespace().Parse(); var remote = File.ReadAllText("Newtonsoft.Remote.txt").WrapWithNamespace().Parse(); var conflicts = Diff3.Compare(ancestor, local, remote); var localDiff = Diff.VisualDiff(conflicts.Local, conflicts.AncestorTree); var remoteDiff = Diff.VisualDiff(conflicts.Remote, conflicts.AncestorTree); conflicts.Conflicts = conflicts.Conflicts.ToList(); Assert.IsTrue(conflicts.Conflicts.Any()); var strrep = conflicts.Conflicts.First().ToString(); }
public void MergeChangesOnlyFrom2WithEmptyBase() { // 0 1 2 3 4 5 var baseList = new List <string>() { }; var mod1List = new List <string>() { }; var mod2List = new List <string>() { "A", "D", "E", "B", "C", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(1, diff3.Count); Assert.AreEqual(Diff3ChangeType.MergeFrom2, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(Span.Invalid, diff3[0].From1); Assert.AreEqual(new Span(0, 5), diff3[0].From2); }
public void MergeDifferentSize() { // 0 1 2 3 4 5 6 var baseList = new List <string>() { "A", "B", "C" }; var mod1List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod2List = new List <string>() { "A", "B", "C", "D", "E", "F", "G" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(3, diff3.Count); // Check 0) Equal "A B C" Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 2), diff3[0].Base); Assert.AreEqual(new Span(0, 2), diff3[0].From1); Assert.AreEqual(new Span(0, 2), diff3[0].From2); // Check 1) MergeFrom1And2 "D E F" Assert.AreEqual(Diff3ChangeType.MergeFrom1And2, diff3[1].ChangeType); Assert.AreEqual(new Span(3, 3), diff3[1].Base); Assert.AreEqual(new Span(3, 5), diff3[1].From1); Assert.AreEqual(new Span(3, 5), diff3[1].From2); // Check 2) MergeFrom2 "G" Assert.AreEqual(Diff3ChangeType.MergeFrom2, diff3[2].ChangeType); Assert.AreEqual(new Span(3, 3), diff3[2].Base); Assert.AreEqual(Span.Invalid, diff3[2].From1); Assert.AreEqual(new Span(6, 6), diff3[2].From2); }
public void MergeMiddle() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod1List = new List <string>() { "A", "B", "C", "D", "E", "F" }; var mod2List = new List <string>() { "A", "B", "X", "D", "E", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(3, diff3.Count); // First set is identical Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 1), diff3[0].Base); Assert.AreEqual(new Span(0, 1), diff3[0].From1); Assert.AreEqual(new Span(0, 1), diff3[0].From2); // Conflict on base:"C", v1:"X", v2:"Y" Assert.AreEqual(Diff3ChangeType.MergeFrom2, diff3[1].ChangeType); Assert.AreEqual(new Span(2, 2), diff3[1].Base); Assert.AreEqual(new Span(2, 2), diff3[1].From1); Assert.AreEqual(new Span(2, 2), diff3[1].From2); // Last set is identical Assert.AreEqual(Diff3ChangeType.Equal, diff3[2].ChangeType); Assert.AreEqual(new Span(3, 5), diff3[2].Base); Assert.AreEqual(new Span(3, 5), diff3[2].From1); Assert.AreEqual(new Span(3, 5), diff3[2].From2); }
public void ConflictMergeMultipleElementsFrom1And2() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "F" }; var mod1List = new List <string>() { "A", "X", "F" }; var mod2List = new List <string>() { "A", "Y", "F" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(3, diff3.Count); // 0) Equal A = A = A Assert.AreEqual(Diff3ChangeType.Equal, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 0), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 0), diff3[0].From2); // 1) Conflict on X Assert.AreEqual(Diff3ChangeType.Conflict, diff3[1].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[1].Base); Assert.AreEqual(new Span(1, 1), diff3[1].From1); Assert.AreEqual(new Span(1, 1), diff3[1].From2); // 4) Equal "F" Assert.AreEqual(Diff3ChangeType.Equal, diff3[2].ChangeType); Assert.AreEqual(new Span(1, 1), diff3[2].Base); Assert.AreEqual(new Span(2, 2), diff3[2].From1); Assert.AreEqual(new Span(2, 2), diff3[2].From2); }
public void MergeFullReplaceFrom1() { // 0 1 2 3 4 5 var baseList = new List <string>() { "A", "B", "C" }; var mod1List = new List <string>() { "D" }; var mod2List = new List <string>() { "A", "B", "C" }; var diff3 = Diff3.Compare(baseList, mod1List, mod2List, EqualityComparer <string> .Default).ToList(); Assert.AreEqual(1, diff3.Count); Assert.AreEqual(Diff3ChangeType.MergeFrom1, diff3[0].ChangeType); Assert.AreEqual(new Span(0, 2), diff3[0].Base); Assert.AreEqual(new Span(0, 0), diff3[0].From1); Assert.AreEqual(new Span(0, 2), diff3[0].From2); }
private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node) { diff3.Type = Diff3NodeType.Collection; var baseItems = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes; var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes; var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes; var itemEqualityComparer = equalityComparer; var node = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode; IEnumerable <Diff3Change> changes; bool recurseDiff = false; // Find an item in any of the list var firstItem = baseItems.FirstOrDefault() ?? asset1Items.FirstOrDefault() ?? asset2Items.FirstOrDefault(); // If we have a DiffUseAsset1Attribute, list of Asset1Node becomes authoritative. var dataVisitMember = node as DataVisitMember; var specificAssetAttribute = dataVisitMember != null?dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffUseSpecificAssetAttribute>(true).FirstOrDefault() : null; if (specificAssetAttribute != null) { var isFromAsset2 = specificAssetAttribute is DiffUseAsset2Attribute; var diffChange = isFromAsset2 ? new Diff3Change { ChangeType = SharpDiff.Diff3ChangeType.MergeFrom2, From2 = new Span(0, asset2Items.Count - 1) } : new Diff3Change { ChangeType = SharpDiff.Diff3ChangeType.MergeFrom1, From1 = new Span(0, asset1Items.Count - 1) }; changes = new[] { diffChange }; // TODO: Try to merge back data of matching nodes } else if (firstItem != null && typeof(IDiffKey).IsAssignableFrom(firstItem.InstanceType)) { // If item implement IDataDiffKey, we will use that as equality key changes = Diff3.Compare( baseItems.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(), asset1Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(), asset2Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList()); recurseDiff = true; } else { // Otherwise, do a full node comparison itemEqualityComparer.Reset(); changes = Diff3.Compare(baseItems, asset1Items, asset2Items, itemEqualityComparer); } foreach (var change in changes) { switch (change.ChangeType) { case SharpDiff.Diff3ChangeType.Equal: for (int i = 0; i < change.Base.Length; i++) { var diff3Node = recurseDiff ? DiffNode(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) : new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.None }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.MergeFrom1: for (int i = 0; i < change.From1.Length; i++) { var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], null) { ChangeType = Diff3ChangeType.MergeFromAsset1 }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.MergeFrom2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = new Diff3Node(null, null, asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset2 }; AddItem(diff3, diff3Node, true); } break; case SharpDiff.Diff3ChangeType.MergeFrom1And2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = recurseDiff ? DiffNode(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) : new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset1And2 }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.Conflict: int baseIndex = change.Base.IsValid ? change.Base.From : -1; int from1Index = change.From1.IsValid ? change.From1.From : -1; int from2Index = change.From2.IsValid ? change.From2.From : -1; // If there are changes only from 1 or 2 or base.Length == list1.Length == list2.Length, then try to make a diff per item // else output the conflict as a full conflict bool tryResolveConflict = false; if (baseIndex >= 0) { if (from1Index >= 0 && from2Index >= 0) { if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length) || (change.From1.Length == change.From2.Length)) { tryResolveConflict = true; } } else if (from1Index >= 0) { tryResolveConflict = change.Base.Length == change.From1.Length; } else if (from2Index >= 0) { tryResolveConflict = change.Base.Length == change.From2.Length; } else { tryResolveConflict = true; } } // Iterate on items while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0)) { var baseItem = GetSafeFromList(baseItems, ref baseIndex, ref change.Base); var asset1Item = GetSafeFromList(asset1Items, ref from1Index, ref change.From1); var asset2Item = GetSafeFromList(asset2Items, ref from2Index, ref change.From2); var diff3Node = tryResolveConflict || recurseDiff? DiffNode(baseItem, asset1Item, asset2Item) : new Diff3Node(baseItem, asset1Item, asset2Item) { ChangeType = Diff3ChangeType.Conflict }; AddItem(diff3, diff3Node, true); } break; } } // Any missing item? (we can detect this only at the end) var newItemCount = diff3.Items != null ? diff3.Items.Count : 0; if (asset1Items.Count != newItemCount) { diff3.ChangeType = Diff3ChangeType.Children; } }
private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node) { var baseItems = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes; var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes; var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes; equalityComparer.Reset(); var changes = Diff3.Compare(baseItems, asset1Items, asset2Items, equalityComparer); foreach (var change in changes) { switch (change.ChangeType) { case SharpDiff.Diff3ChangeType.Equal: for (int i = 0; i < change.Base.Length; i++) { var diff3Node = new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.None }; AddItem(diff3, diff3Node); } break; case SharpDiff.Diff3ChangeType.MergeFrom1: for (int i = 0; i < change.From1.Length; i++) { var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], null) { ChangeType = Diff3ChangeType.MergeFromAsset1 }; AddItem(diff3, diff3Node); } break; case SharpDiff.Diff3ChangeType.MergeFrom2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = new Diff3Node(null, null, asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset2 }; AddItem(diff3, diff3Node); } break; case SharpDiff.Diff3ChangeType.MergeFrom1And2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset1And2 }; AddItem(diff3, diff3Node); } break; case SharpDiff.Diff3ChangeType.Conflict: int baseIndex = change.Base.IsValid ? change.Base.From : -1; int from1Index = change.From1.IsValid ? change.From1.From : -1; int from2Index = change.From2.IsValid ? change.From2.From : -1; // If there are changes only from 1 or 2 or base.Length == list1.Length == list2.Length, then try to make a diff per item // else output the conflict as a full conflict bool tryResolveConflict = false; if (baseIndex >= 0) { if (from1Index >= 0 && from2Index >= 0) { if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length) || (change.From1.Length == change.From2.Length)) { tryResolveConflict = true; } } else if (from1Index >= 0) { tryResolveConflict = change.Base.Length == change.From1.Length; } else if (from2Index >= 0) { tryResolveConflict = change.Base.Length == change.From2.Length; } else { tryResolveConflict = true; } } // Iterate on items while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0)) { var baseItem = GetSafeFromList(baseItems, ref baseIndex, ref change.Base); var asset1Item = GetSafeFromList(asset1Items, ref from1Index, ref change.From1); var asset2Item = GetSafeFromList(asset2Items, ref from2Index, ref change.From2); var diff3Node = tryResolveConflict ? DiffNode(baseItem, asset1Item, asset2Item) : new Diff3Node(baseItem, asset1Item, asset2Item) { ChangeType = Diff3ChangeType.Conflict }; AddItem(diff3, diff3Node); } break; } } // Order by descending index if (diff3.Items != null) { diff3.Items.Sort((left, right) => { int leftAsset1Index = left.Asset1Node != null ? ((DataVisitListItem)left.Asset1Node).Index : -1; int rightAsset1Index = right.Asset1Node != null ? ((DataVisitListItem)right.Asset1Node).Index : -1; return(rightAsset1Index.CompareTo(leftAsset1Index)); }); } }