/// <summary> /// Finds the colliding changes. /// </summary> /// <param name="current">The current change.</param> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <returns></returns> private static CollidingChanges FindCollidingChanges(IMergableChange current, MergableChangeset changeset1, MergableChangeset changeset2) { var collidingChangesFrom1 = new MergableChangeset(); var collidingChangesFrom2 = new MergableChangeset(); bool swapped = false; while (true) { IMergableChange colliding = FindCollision(changeset2, current.Start, current.AfterFinish); if ((colliding == null) || colliding.Equals(current)) { if (swapped) { Utils.Swap(ref collidingChangesFrom1, ref collidingChangesFrom2); } return(new CollidingChanges(collidingChangesFrom1, collidingChangesFrom2)); } collidingChangesFrom2.Add(colliding); current = colliding; Utils.Swap(ref changeset1, ref changeset2); Utils.Swap(ref collidingChangesFrom1, ref collidingChangesFrom2); swapped = !swapped; } }
/// <summary> /// Processes the collision. /// </summary> /// <param name="source">The source.</param> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> protected override IChange ProcessCollision(IReadOnlyList <string> source, IMergableChange change1, IMergableChange change2) { IChange mergedChange = TryMerge(change1, change2); return(mergedChange ?? base.ProcessCollision(source, change1, change2)); }
/// <summary> /// Collects the changes. /// </summary> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <param name="source">The source.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException"></exception> private IEnumerable <IChange> CollectChanges(MergableChangeset changeset1, MergableChangeset changeset2, IReadOnlyList <string> source) { if ((changeset1 == null) || (changeset2 == null) || (source == null)) { throw new ArgumentNullException(); } changeset1.Verify(source.Count); changeset2.Verify(source.Count); var lines = new SortedSet <int>(changeset1.Keys.Union(changeset2.Keys)); var removedLines = new HashSet <int>(); bool swapped = false; foreach (int line in lines.Where(l => !removedLines.Contains(l))) { if (!changeset1.ContainsKey(line)) { Utils.Swap(ref changeset1, ref changeset2); swapped = !swapped; } IMergableChange currentChange = changeset1.Extract(line); CollidingChanges collidingChanges = FindCollidingChanges(currentChange, changeset1, changeset2); if (collidingChanges.IsEmpty()) { yield return(currentChange); continue; } IMergableChange collidingChange = collidingChanges.TryGetSingle(); if (collidingChange != null) { removedLines.Add(collidingChange.Start); if (swapped) { Utils.Swap(ref currentChange, ref collidingChange); } yield return(ProcessCollision(source, currentChange, collidingChange)); } else { foreach (int l in collidingChanges.Keys) { removedLines.Add(l); } collidingChanges.ChangesFrom1.Add(currentChange); yield return(swapped ? ProcessCollision(source, collidingChanges.ChangesFrom2, collidingChanges.ChangesFrom1) : ProcessCollision(source, collidingChanges.ChangesFrom1, collidingChanges.ChangesFrom2)); } } }
/// <summary> /// Tries to merge. /// </summary> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> private static IChange TryMerge(IMergableChange change1, IMergableChange change2) { if (change1.Start != change2.Start) { return null; } var addition = Utils.TryCastOneOf<Addition>(change1, change2); if (addition == null) { return null; } var removal = Utils.TryCastOneOf<Removal>(change1, change2); if (removal == null) { return null; } return new Replacement(removal.Start, removal.RemovedAmount, addition.NewContent.ToArray()); }
/// <summary> /// Initializes a new instance of the <see cref="Conflict"/> class. /// </summary> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <param name="source">The source.</param> /// <param name="conflictBlocks">The conflict blocks.</param> /// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// </exception> /// <exception cref="System.ArgumentException"></exception> internal Conflict(IMergableChange change1, IMergableChange change2, IReadOnlyList <string> source, ConflictBlocks conflictBlocks) { if ((change1 == null) || (change2 == null) || (source == null)) { throw new ArgumentNullException(); } int start = Math.Min(change1.Start, change2.Start); if (start > source.Count) { throw new ArgumentOutOfRangeException(); } int afterFinish = Math.Max(change1.AfterFinish, change2.AfterFinish); if (afterFinish > source.Count) { throw new ArgumentOutOfRangeException(); } if ((change1.Start >= change2.AfterFinish) || (change2.Start >= change1.AfterFinish)) { if (change1.Start != change2.Start) { throw new ArgumentException(); } } int removedAmount = afterFinish - start; IEnumerable <string> originalBlock = Utils.GetSubArray(source, start, afterFinish); IEnumerable <string> changedBlock1 = GetChangedBlock(source, start, afterFinish, change1); IEnumerable <string> changedBlock2 = GetChangedBlock(source, start, afterFinish, change2); IEnumerable <string> newContent = GetNewContent(originalBlock, changedBlock1, changedBlock2, conflictBlocks); Initialize(start, removedAmount, newContent.ToArray()); }
/// <summary> /// Tries to merge. /// </summary> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> private static IChange TryMerge(IMergableChange change1, IMergableChange change2) { if (change1.Start != change2.Start) { return(null); } var addition = Utils.TryCastOneOf <Addition>(change1, change2); if (addition == null) { return(null); } var removal = Utils.TryCastOneOf <Removal>(change1, change2); if (removal == null) { return(null); } return(new Replacement(removal.Start, removal.RemovedAmount, addition.NewContent.ToArray())); }
/// <summary> /// Processes the collision. /// </summary> /// <param name="source">The source.</param> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> protected override IChange ProcessCollision(IReadOnlyList<string> source, IMergableChange change1, IMergableChange change2) { IChange mergedChange = TryMerge(change1, change2); return mergedChange ?? base.ProcessCollision(source, change1, change2); }
/// <summary> /// Finds the colliding changes. /// </summary> /// <param name="current">The current change.</param> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <returns></returns> private static CollidingChanges FindCollidingChanges(IMergableChange current, MergableChangeset changeset1, MergableChangeset changeset2) { var collidingChangesFrom1 = new MergableChangeset(); var collidingChangesFrom2 = new MergableChangeset(); bool swapped = false; while (true) { IMergableChange colliding = FindCollision(changeset2, current.Start, current.AfterFinish); if ((colliding == null) || colliding.Equals(current)) { if (swapped) { Utils.Swap(ref collidingChangesFrom1, ref collidingChangesFrom2); } return new CollidingChanges(collidingChangesFrom1, collidingChangesFrom2); } collidingChangesFrom2.Add(colliding); current = colliding; Utils.Swap(ref changeset1, ref changeset2); Utils.Swap(ref collidingChangesFrom1, ref collidingChangesFrom2); swapped = !swapped; } }
/// <summary> /// Processes the collision. /// </summary> /// <param name="source">The source.</param> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> protected virtual IChange ProcessCollision(IReadOnlyList<string> source, IMergableChange change1, IMergableChange change2) { return new Conflict(change1, change2, source, ConflictBlocks); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Helpers /// <summary> /// Processes the collision. /// </summary> /// <param name="source">The source.</param> /// <param name="change1">The change1.</param> /// <param name="change2">The change2.</param> /// <returns></returns> protected virtual IChange ProcessCollision(IReadOnlyList <string> source, IMergableChange change1, IMergableChange change2) { return(new Conflict(change1, change2, source, ConflictBlocks)); }