/// <summary> /// Initializes a new instance of the <see cref="Conflict"/> class. /// </summary> /// <param name="source">The source.</param> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <param name="conflictBlocks">The conflict blocks.</param> /// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentOutOfRangeException"></exception> internal Conflict(IReadOnlyList <string> source, MergableChangeset changeset1, MergableChangeset changeset2, ConflictBlocks conflictBlocks) { if ((source == null) || (changeset1 == null) || (changeset2 == null)) { throw new ArgumentNullException(); } var allChanges = new HashSet <IMergableChange>(changeset1.Values.Union(changeset2.Values)); int start = allChanges.Min(c => c.Start); int afterFinish = allChanges.Max(c => c.AfterFinish); if ((start > source.Count) || (afterFinish > source.Count)) { throw new ArgumentOutOfRangeException(); } int removedAmount = afterFinish - start; IEnumerable <string> originalBlock = Utils.GetSubArray(source, start, afterFinish); IEnumerable <string> changedBlock1 = GetChangedBlock(source, start, afterFinish, changeset1); IEnumerable <string> changedBlock2 = GetChangedBlock(source, start, afterFinish, changeset2); IEnumerable <string> newContent = GetNewContent(originalBlock, changedBlock1, changedBlock2, conflictBlocks); Initialize(start, removedAmount, newContent.ToArray()); }
/// <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> /// 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> /// Merges the specified changesets. /// </summary> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <param name="source">The source.</param> /// <returns></returns> public Changeset<IChange> Merge(MergableChangeset changeset1, MergableChangeset changeset2, IReadOnlyList<string> source) { var result = new Changeset<IChange>(); IEnumerable<IChange> changes = CollectChanges(changeset1, changeset2, source); foreach (IChange change in changes) { result.Add(change.Start, change); } return result; }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// #region IChangeSetMerger implementation /// <summary> /// Merges the specified changesets. /// </summary> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <param name="source">The source.</param> /// <returns></returns> public Changeset <IChange> Merge(MergableChangeset changeset1, MergableChangeset changeset2, IReadOnlyList <string> source) { var result = new Changeset <IChange>(); IEnumerable <IChange> changes = CollectChanges(changeset1, changeset2, source); foreach (IChange change in changes) { result.Add(change.Start, change); } return(result); }
/// <summary> /// Finds the collision. /// </summary> /// <param name="changeset">The changeset.</param> /// <param name="start">The first line number.</param> /// <param name="afterFinish">The number of the line that is one past last.</param> /// <returns></returns> private static IMergableChange FindCollision(MergableChangeset changeset, int start, int afterFinish) { if (changeset.ContainsKey(start)) { return(changeset.Extract(start)); } for (int i = start + 1; i < afterFinish; ++i) { if (changeset.ContainsKey(i)) { return(changeset.Extract(i)); } } return(null); }
/// <summary> /// Performs the merge. /// </summary> /// <param name="source">The source content.</param> /// <param name="changed1">The first changed content.</param> /// <param name="changed2">The second changed content.</param> /// <param name="merger">The merger.</param> /// <returns></returns> public static Result Merge(IReadOnlyList <string> source, IReadOnlyList <string> changed1, IReadOnlyList <string> changed2, IChangesetMerger merger) { if (merger == null) { throw new ArgumentNullException(); } var changeset1 = new MergableChangeset(source, changed1); var changeset2 = new MergableChangeset(source, changed2); Changeset <IChange> merged = merger.Merge(changeset1, changeset2, source); IEnumerable <string> result = merged.Apply(source); return(new Result(merged, result)); }
/// <summary> /// Performs the merge. /// </summary> /// <param name="source">The source content.</param> /// <param name="changed1">The first changed content.</param> /// <param name="changed2">The second changed content.</param> /// <param name="merger">The merger.</param> /// <returns></returns> public static Result Merge(IReadOnlyList<string> source, IReadOnlyList<string> changed1, IReadOnlyList<string> changed2, IChangesetMerger merger) { if (merger == null) { throw new ArgumentNullException(); } var changeset1 = new MergableChangeset(source, changed1); var changeset2 = new MergableChangeset(source, changed2); Changeset<IChange> merged = merger.Merge(changeset1, changeset2, source); IEnumerable<string> result = merged.Apply(source); return new Result(merged, result); }
/// <summary> /// Gets the changed block. /// </summary> /// <param name="source">The source.</param> /// <param name="start">The start.</param> /// <param name="afterFinish">The after finish.</param> /// <param name="changeset">The changeset.</param> /// <returns></returns> private static IEnumerable <string> GetChangedBlock(IReadOnlyList <string> source, int start, int afterFinish, MergableChangeset changeset) { int currentLine = start; foreach (IMergableChange change in changeset.OrderedValues) { foreach (string line in GetChangedBlock(source, currentLine, change.AfterFinish, change)) { yield return(line); } currentLine = change.AfterFinish; } for (int i = currentLine; i < afterFinish; ++i) { yield return(source[i]); } }
/// <summary> /// Merges the specified changesets. /// </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> public Changeset <IChange> Merge(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 result = new Changeset <IChange>(); foreach (int key in changeset1.Keys) { result.Add(key, changeset1[key]); } return(result); }
/// <summary> /// Initializes a new instance of the <see cref="CollidingChanges"/> class. /// </summary> /// <param name="changesFrom1">The changes changeset 1.</param> /// <param name="changesFrom2">The changes changeset 2.</param> internal CollidingChanges(MergableChangeset changesFrom1, MergableChangeset changesFrom2) { ChangesFrom1 = changesFrom1; ChangesFrom2 = changesFrom2; }
/// <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 content.</param> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <returns></returns> protected virtual IChange ProcessCollision(IReadOnlyList<string> source, MergableChangeset changeset1, MergableChangeset changeset2) { return new Conflict(source, changeset1, changeset2, ConflictBlocks); }
/// <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> /// Finds the collision. /// </summary> /// <param name="changeset">The changeset.</param> /// <param name="start">The first line number.</param> /// <param name="afterFinish">The number of the line that is one past last.</param> /// <returns></returns> private static IMergableChange FindCollision(MergableChangeset changeset, int start, int afterFinish) { if (changeset.ContainsKey(start)) { return changeset.Extract(start); } for (int i = start + 1; i < afterFinish; ++i) { if (changeset.ContainsKey(i)) { return changeset.Extract(i); } } return null; }
/// <summary> /// Processes the collision. /// </summary> /// <param name="source">The source content.</param> /// <param name="changeset1">The changeset1.</param> /// <param name="changeset2">The changeset2.</param> /// <returns></returns> protected virtual IChange ProcessCollision(IReadOnlyList <string> source, MergableChangeset changeset1, MergableChangeset changeset2) { return(new Conflict(source, changeset1, changeset2, ConflictBlocks)); }