예제 #1
0
        /// <summary>
        /// Generates the diff, one line of output at a time.
        /// </summary>
        /// <returns>
        /// A collection of <see cref="AlignedDiffChange"/> objects, one for
        /// each line in the first or second collection (sometimes one instance for a line
        /// from both, when lines are equal or similar.)
        /// </returns>
        public IEnumerable <AlignedDiffChange> Generate()
        {
            int i1 = 0;
            int i2 = 0;

            foreach (Diff2Change section in Diff2.Compare <T>(_Collection1, _Collection2, _EqualityComparer))
            {
                if (section.Equal)
                {
                    for (int index = 0; index < section.Length1; index++)
                    {
                        yield return(new AlignedDiffChange(ChangeType.Same, i1, i2));

                        i1++;
                        i2++;
                    }
                }
                else
                {
                    bool deletePlusAdd = true;
                    if (section.Length1 > 0 && section.Length2 > 0)
                    {
                        AlignedDiffChange[] alignedChanges = TryAlignChanges(section, i1, i2);
                        if (alignedChanges.Length > 0)
                        {
                            deletePlusAdd = false;
                            foreach (var change in alignedChanges)
                            {
                                yield return(change);
                            }
                            i1 += section.Length1;
                            i2 += section.Length2;
                        }
                    }

                    if (deletePlusAdd)
                    {
                        for (int index = 0; index < section.Length1; index++)
                        {
                            yield return(new AlignedDiffChange(ChangeType.Deleted, i1, -1));

                            i1++;
                        }
                        for (int index = 0; index < section.Length2; index++)
                        {
                            yield return(new AlignedDiffChange(ChangeType.Added, -1, i2));

                            i2++;
                        }
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Compares the two collections and generate a diff.
        /// </summary>
        /// <typeparam name="T">Type of the element in the list</typeparam>
        /// <param name="left">The first collection.</param>
        /// <param name="right">The second collection.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns>An enumeration of <see cref="Diff2Change"/>.</returns>
        public static IEnumerable <Diff2Change> Compare <T>(IList <T> left, IList <T> right, IEqualityComparer <T> comparer)
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }
            if (comparer == null)
            {
                throw new ArgumentNullException("comparer");
            }

            var diff = new Diff2 <T>(left, right, comparer);

            return(diff.Generate());
        }
예제 #3
0
        /// <summary>
        /// Does a similarity comparison between the two values and returns their
        /// similarity, a value ranging from 0.0 to 1.0, where 0.0 means they're
        /// completely different and 1.0 means they have the same value.
        /// </summary>
        /// <param name="value1">
        /// The first value to compare.
        /// </param>
        /// <param name="value2">
        /// The second value to compare.
        /// </param>
        /// <returns>
        /// A value ranging from 0.0 to 1.0, where 0.0 means they're
        /// completely different and 1.0 means they have the same value.
        /// </returns>
        public double Compare(string value1, string value2)
        {
            if (ReferenceEquals(value1, value2))
            {
                return(1.0);
            }

            value1 = (value1 ?? String.Empty);
            value2 = (value2 ?? String.Empty);

            if (value1.Length == 0 && value2.Length == 0)
            {
                return(1.0);
            }
            if (value1.Length == 0 || value2.Length == 0)
            {
                return(0.0);
            }

            int same = Diff2.Compare(value1.ToCharArray(), value2.ToCharArray()).Where(s => s.Equal).Sum(s => s.Length1);

            return((same * 2.0) / (value1.Length + value2.Length + 0.0));
        }
예제 #4
0
        /// <summary>
        /// Determines if the two values are similar enough to align them
        /// as a change, instead of not aligning them but reporting them
        /// as a delete plus an add instead.
        /// </summary>
        /// <param name="value1">
        /// The first value to compare against <paramref name="value2"/>.
        /// </param>
        /// <param name="value2">
        /// The second value to compare against <paramref name="value1"/>.
        /// </param>
        /// <returns>
        /// <c>true</c> if the two values are similar enough to report
        /// them as a change; false if the two values aren't similar enough
        /// but needs to be reported as a delete plus an add.
        /// </returns>
        public bool CanAlign(string value1, string value2)
        {
            if (ReferenceEquals(value1, value2))
            {
                return(true);
            }

            value1 = (value1 ?? String.Empty);
            value2 = (value2 ?? String.Empty);

            if (value1.Length == 0 && value2.Length == 0)
            {
                return(true);
            }
            if (value1.Length == 0 || value2.Length == 0)
            {
                return(false);
            }

            var diff = Diff2.Compare(value1.ToCharArray(), value2.ToCharArray());

            return(_DiffPredicate(value1, value2, diff));
        }
예제 #5
0
        private List <Diff3Change> FixPreviousConflictAsMerge(List <Diff3Change> changes)
        {
            List <Diff3Change> toChanges = null;

            for (int index = 0; index < changes.Count; index++)
            {
                var diff3 = changes[index];

                if (diff3.ChangeType != Diff3ChangeType.Conflict)
                {
                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                    continue;
                }

                if (!diff3.Base.IsValid && diff3.From1.IsValid && !diff3.From2.IsValid)
                {
                    diff3.ChangeType = Diff3ChangeType.MergeFrom1;
                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                }
                else if (!diff3.Base.IsValid && !diff3.From1.IsValid && diff3.From2.IsValid)
                {
                    diff3.ChangeType = Diff3ChangeType.MergeFrom2;
                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                }
                else if (diff3.Base.IsValid && diff3.From1.IsValid && !diff3.From2.IsValid)
                {
                    if (IsRangeEqual(baseList, diff3.Base, modified1List, diff3.From1))
                    {
                        diff3.ChangeType = Diff3ChangeType.MergeFrom2;
                    }

                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                }
                else if (diff3.Base.IsValid && diff3.From2.IsValid && !diff3.From1.IsValid)
                {
                    if (IsRangeEqual(baseList, diff3.Base, modified2List, diff3.From2))
                    {
                        diff3.ChangeType = Diff3ChangeType.MergeFrom1;
                    }

                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                }
                else if (diff3.From1.IsValid && diff3.From2.IsValid)
                {
                    // If there is no base, we will try to merge 1 and 2 together directly
                    if (!diff3.Base.IsValid)
                    {
                        if (toChanges == null)
                        {
                            toChanges = new List <Diff3Change>(changes.Count);
                            for (int i = 0; i < index; i++)
                            {
                                toChanges.Add(changes[i]);
                            }
                        }

                        var from1Range = diff3.From1;
                        var from2Range = diff3.From2;

                        if (subList1 == null)
                        {
                            subList1 = new List <T>(Math.Max(10, from1Range.Length));
                        }
                        if (subList2 == null)
                        {
                            subList2 = new List <T>(Math.Max(10, from2Range.Length));
                        }
                        subList1.Clear();
                        subList2.Clear();
                        for (int i = from1Range.From; i <= from1Range.To; i++)
                        {
                            subList1.Add(modified1List[i]);
                        }
                        for (int i = from2Range.From; i <= from2Range.To; i++)
                        {
                            subList2.Add(modified2List[i]);
                        }

                        var diffModified1Modified2 = Diff2.Compare(subList1, subList2, comparer).ToList();
                        int diff1Index             = from1Range.From;
                        int diff2Index             = from2Range.From;

                        // Try to evaluate the base index to give an indication where the merge would be inserted in base
                        var baseIndex = index > 0 ? changes[index - 1].Base.IsValid ? changes[index - 1].Base.To + 1 : 0 : 0;
                        var spanBase  = new Span(baseIndex, baseIndex);
                        foreach (var diff2Change in diffModified1Modified2)
                        {
                            if (diff2Change.Equal)
                            {
                                AddChangeType(toChanges, Diff3ChangeType.MergeFrom1And2, spanBase, new Span(diff1Index, diff1Index + diff2Change.Length1 - 1), new Span(diff2Index, diff2Index + diff2Change.Length2 - 1), null, null);
                            }
                            else if (diff2Change.Length1 == 0)
                            {
                                AddChangeType(toChanges, Diff3ChangeType.MergeFrom2, spanBase, Span.Invalid, new Span(diff2Index, diff2Index + diff2Change.Length2 - 1), null, null);
                            }
                            else if (diff2Change.Length2 == 0)
                            {
                                AddChangeType(toChanges, Diff3ChangeType.Conflict, spanBase, new Span(diff1Index, diff1Index + diff2Change.Length1 - 1), Span.Invalid, null, null);
                            }
                            else
                            {
                                AddChangeType(toChanges, Diff3ChangeType.Conflict, spanBase, new Span(diff1Index, diff1Index + diff2Change.Length1 - 1), new Span(diff2Index, diff2Index + diff2Change.Length2 - 1), null, null);
                            }
                            diff1Index += diff2Change.Length1;
                            diff2Index += diff2Change.Length2;
                        }
                    }
                    else
                    {
                        // if base = v1 then MergeFrom2
                        if (diff3.IsBaseEqualFrom1.HasValue && diff3.IsBaseEqualFrom1.Value)
                        {
                            diff3.ChangeType = Diff3ChangeType.MergeFrom2;
                        }
                        // if base = v2  then MergeFrom1
                        else if (diff3.IsBaseEqualFrom2.HasValue && diff3.IsBaseEqualFrom2.Value)
                        {
                            diff3.ChangeType = Diff3ChangeType.MergeFrom1;
                        }
                        else if (diff3.Base.Length == diff3.From1.Length && diff3.Base.Length == diff3.From2.Length)
                        {
                            // Else if have a base != v1 and base != v2
                            // Check that if v1 = v2 and range is same for (base,v1,v2) then
                            // this can be considered as a MergeFrom1And2

                            if (diff3.IsBaseEqualFrom1.HasValue && diff3.IsBaseEqualFrom1.Value && (!diff3.IsBaseEqualFrom2.HasValue || !diff3.IsBaseEqualFrom2.Value))
                            {
                                diff3.ChangeType = Diff3ChangeType.MergeFrom2;
                            }
                            else if (diff3.IsBaseEqualFrom2.HasValue && diff3.IsBaseEqualFrom2.Value && (!diff3.IsBaseEqualFrom1.HasValue || !diff3.IsBaseEqualFrom1.Value))
                            {
                                diff3.ChangeType = Diff3ChangeType.MergeFrom1;
                            }
                            else
                            {
                                bool isFrom1AndFrom2Equal = true;
                                for (int i = 0; i < diff3.Base.Length; i++)
                                {
                                    if (!comparer.Equals(modified1List[diff3.From1.From + i], modified2List[diff3.From2.From + i]))
                                    {
                                        isFrom1AndFrom2Equal = false;
                                        break;
                                    }
                                }
                                if (isFrom1AndFrom2Equal)
                                {
                                    diff3.ChangeType = Diff3ChangeType.MergeFrom1And2;
                                }
                            }
                        }

                        if (toChanges != null)
                        {
                            AddChangeType(toChanges, diff3);
                        }
                    }
                }
                else
                {
                    if (toChanges != null)
                    {
                        AddChangeType(toChanges, diff3);
                    }
                }
            }
            return(toChanges ?? changes);
        }
예제 #6
0
        public List <Diff3Change> Generate()
        {
            var diffBaseModified1 = ConvertDiffChangeWithNoRange(Diff2.Compare(baseList, modified1List, comparer)).ToList();
            var diffBaseModified2 = ConvertDiffChangeWithNoRange(Diff2.Compare(baseList, modified2List, comparer)).ToList();

            var changes = new List <Diff3Change>();

            int index1     = 0;
            int index2     = 0;
            int baseIndex1 = 0;
            int baseIndex2 = 0;
            int diffIndex1 = 0;
            int diffIndex2 = 0;

            while (diffIndex1 < diffBaseModified1.Count && diffIndex2 < diffBaseModified2.Count)
            {
                var diff1 = diffBaseModified1[diffIndex1];
                var diff2 = diffBaseModified2[diffIndex2];

                // If element1 and element2 are both equals to the base element
                if (diff1.Equal && diff2.Equal && baseIndex1 == baseIndex2)
                {
                    AddChangeType(changes, Diff3ChangeType.Equal, new Span(baseIndex1, baseIndex2), new Span(index1, index1 + diff1.Length2 - 1), new Span(index2, index2 + diff2.Length2 - 1), true, true);

                    baseIndex1 += diff1.Length1;
                    index1     += diff1.Length2;
                    baseIndex2 += diff2.Length1;
                    index2     += diff2.Length2;
                    diffIndex1++;
                    diffIndex2++;
                }
                else
                {
                    var baseRange  = Span.Invalid;
                    var from1Range = Span.Invalid;
                    var from2Range = Span.Invalid;

                    bool change1 = false;
                    bool change2 = false;

                    var diff1Equal = true;
                    var diff2Equal = true;

                    // Try to advance as much as possible on both sides until we reach a common point (baseIndex1 == baseIndex2)
                    // This will form a single conflict node
                    while (true)
                    {
                        if (diffIndex1 < diffBaseModified1.Count && !diff1.Equal || baseIndex2 > baseIndex1)
                        {
                            // Advance in list1
                            change1     = true;
                            diff1Equal &= diff1.Equal;

                            if (diff1.Length2 > 0)
                            {
                                from1Range = new Span(from1Range != Span.Invalid ? from1Range.From : index1, index1 + diff1.Length2 - 1);
                            }

                            if (diff1.Length1 > 0)
                            {
                                baseRange = new Span(baseRange != Span.Invalid ? baseRange.From : baseIndex1, baseIndex1 + diff1.Length1 - 1);
                            }

                            baseIndex1 += diff1.Length1;
                            index1     += diff1.Length2;

                            if (++diffIndex1 < diffBaseModified1.Count)
                            {
                                diff1 = diffBaseModified1[diffIndex1];
                            }
                        }
                        else if (diffIndex2 < diffBaseModified2.Count && !diff2.Equal || baseIndex1 > baseIndex2)
                        {
                            // Advance in list2
                            change2     = true;
                            diff2Equal &= diff2.Equal;

                            if (diff2.Length2 > 0)
                            {
                                from2Range = new Span(from2Range != Span.Invalid ? from2Range.From : index2, index2 + diff2.Length2 - 1);
                            }

                            if (diff2.Length1 > 0)
                            {
                                baseRange = new Span(baseRange != Span.Invalid ? baseRange.From : baseIndex2, baseIndex2 + diff2.Length1 - 1);
                            }

                            baseIndex2 += diff2.Length1;
                            index2     += diff2.Length2;

                            if (++diffIndex2 < diffBaseModified2.Count)
                            {
                                diff2 = diffBaseModified2[diffIndex2];
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    AddChangeType(changes, Diff3ChangeType.Conflict, baseRange, from1Range, from2Range, change1 ? (bool?)diff1Equal : null, change2 ? (bool?)diff2Equal : null);
                }
            }

            // Not sure this could happen, but in case, output conflicts instead of merge if original base-v1 and base-v2 diffs were not entirely processed
            bool isInConflict = diffIndex1 < diffBaseModified1.Count && diffIndex2 < diffBaseModified2.Count;

            // Process remaining diffs from1
            while (diffIndex1 < diffBaseModified1.Count)
            {
                var diff1 = diffBaseModified1[diffIndex1];
                AddChangeType(changes, isInConflict ? Diff3ChangeType.Conflict : Diff3ChangeType.MergeFrom1, new Span(baseIndex1, baseIndex1), new Span(index1, index1 + diff1.Length2 - 1), Span.Invalid, null, null);

                baseIndex1 += diff1.Length1;
                index1     += diff1.Length2;
                diffIndex1++;
            }

            // Process remaining diffs from2
            while (diffIndex2 < diffBaseModified2.Count)
            {
                var diff2 = diffBaseModified2[diffIndex2];
                AddChangeType(changes, isInConflict ? Diff3ChangeType.Conflict : Diff3ChangeType.MergeFrom2, new Span(baseIndex2, baseIndex2), Span.Invalid, new Span(index2, index2 + diff2.Length2 - 1), null, null);

                baseIndex2 += diff2.Length1;
                index2     += diff2.Length2;
                diffIndex2++;
            }

            return(FixPreviousConflictAsMerge(changes));
        }