Ejemplo n.º 1
0
        /// <summary>
        /// Gets th edit script of this line in comparison to its <see cref="Counterpart"/>.
        /// The change edit script can then be used to color each letter position in the line
        /// to indicate how one line can completely match the other using character based
        /// change operations (insert, delete, change, none).
        ///
        /// The method should only be invoked on demand (when a line is actually
        /// displayed) - we should wait with pulling it until we have to have it for rendering.
        ///
        /// This object will NOT cache the edit script so the caller should implement an external
        /// caching algorithm to avoid multiple computations of the same answer.
        ///
        /// Getting all intra-line diffs at once makes the whole process into an O(n^2) operation
        /// instead of just an O(n) operation for line-by-line diffs.  So we try to defer the
        /// extra work until the user requests to see the changed line.  It's still
        /// the same amount of work if the user views every line, but it makes the
        /// user interface more responsive to split it up like this.
        /// </summary>
        /// <param name="options"></param>
        /// <returns></returns>
        public EditScript GetChangeEditScript(ChangeDiffOptions options)
        {
            EditScript _changeEditScript = null;

            if (            //_changeEditScript == null &&
                _editType == EditType.Change && this.Counterpart != null)
            {
                if (this.FromA)
                {
                    int trimCountA, trimCountB;
                    MyersDiff <char> diff = new MyersDiff <char>(
                        GetCharactersToDiff(this.text, options, out trimCountA),
                        GetCharactersToDiff(this.Counterpart.text, options, out trimCountB),
                        false);                         // We don't want Change edits; just Deletes and Inserts.

                    _changeEditScript = diff.Execute(null);

                    // If we trimmed/ignored leading whitespace, we have to offset each Edit to account for that.
                    foreach (Edit edit in _changeEditScript)
                    {
                        edit.Offset(trimCountA, trimCountB);
                    }
                }
                else if (this.Counterpart.FromA && this.Counterpart.Counterpart == this)
                {
                    // Defer to the A line because its edit script changes A into B.
                    _changeEditScript = this.Counterpart.GetChangeEditScript(options);
                }
            }

            return(_changeEditScript);
        }
Ejemplo n.º 2
0
        /**
         * Tests the performance of MyersDiff for texts which are similar (not
         * random data). The CPU time is measured and returned. Because of bad
         * accuracy of CPU time information the diffs are repeated. During each
         * repetition the interim CPU time is checked. The diff operation is
         * repeated until we have seen the CPU time clock changed its value at least
         * {@link #minCPUTimerTicks} times.
         *
         * @param characters
         *            the size of the diffed character sequences.
         * @return performance data
         */
        private PerfData test(int characters)
        {
            PerfData  ret            = new PerfData();
            string    a              = DiffTestDataGenerator.generateSequence(characters, 971, 3);
            string    b              = DiffTestDataGenerator.generateSequence(characters, 1621, 5);
            CharArray ac             = new CharArray(a);
            CharArray bc             = new CharArray(b);
            MyersDiff myersDiff      = null;
            int       cpuTimeChanges = 0;
            long      lastReadout    = 0;
            long      interimTime    = 0;
            int       repetitions    = 0;

            stopwatch.start();
            while (cpuTimeChanges < minCPUTimerTicks && interimTime < longTaskBoundary)
            {
                myersDiff = new MyersDiff(ac, bc);
                repetitions++;
                interimTime = stopwatch.readout();
                if (interimTime != lastReadout)
                {
                    cpuTimeChanges++;
                    lastReadout = interimTime;
                }
            }
            ret.runningTime = stopwatch.stop() / repetitions;
            ret.N           = (ac.size() + bc.size());
            ret.D           = myersDiff.getEdits().size();

            return(ret);
        }
Ejemplo n.º 3
0
        public LineAndCommit[] Blame(Commit commit, string path)
        {
            var leaf = commit.Tree[path] as Leaf;

            if (leaf == null)
            {
                throw new ArgumentException("The given path does not exist in this commit: " + path);
            }

            byte[]  data    = leaf.RawData;
            IntList lineMap = RawParseUtils.lineMap(data, 0, data.Length);
            var     lines   = new LineAndCommit[lineMap.size()];
            var     curText = new RawText(data);

            Commit prevAncestor = commit;

            Leaf   prevLeaf   = null;
            Commit prevCommit = null;
            int    emptyLines = lineMap.size();

            foreach (Commit ancestor in commit.Ancestors)
            {
                var cleaf = ancestor.Tree[path] as Leaf;
                if (prevCommit != null && (cleaf == null || cleaf.Hash != prevLeaf.Hash))
                {
                    byte[] prevData = prevLeaf.RawData;
                    if (prevData == null)
                    {
                        break;
                    }
                    var prevText = new RawText(prevData);
                    var differ   = new MyersDiff(prevText, curText);
                    foreach (Edit e in differ.getEdits())
                    {
                        for (int n = e.BeginB; n < e.EndB; n++)
                        {
                            if (lines[n] == null)
                            {
                                lines[n] = CreateViewModel(GetLine(commit.Encoding, curText, n), prevCommit);
                                emptyLines--;
                            }
                        }
                    }
                    if (cleaf == null || emptyLines <= 0)
                    {
                        break;
                    }
                }
                prevCommit = ancestor;
                prevLeaf   = cleaf;
            }
            for (int n = 0; n < lines.Length; n++)
            {
                if (lines[n] == null)
                {
                    lines[n] = CreateViewModel(GetLine(commit.Encoding, curText, n), prevAncestor);
                }
            }
            return(lines);
        }
Ejemplo n.º 4
0
        public Commit[] Blame(string path)
        {
            Leaf leaf = Tree [path] as Leaf;

            if (leaf == null)
            {
                throw new ArgumentException("The given path does not exist in this commit: " + path);
            }
            byte[] data      = leaf.RawData;
            int    lineCount = RawParseUtils.lineMap(data, 0, data.Length).size();

            Commit[] lines        = new Commit [lineCount];
            var      curText      = new RawText(data);
            Commit   prevAncestor = this;

            Leaf   prevLeaf   = null;
            Commit prevCommit = null;
            int    emptyLines = lineCount;

            foreach (Commit ancestor in Ancestors)
            {
                Leaf cleaf = ancestor.Tree [path] as Leaf;
                if (prevCommit != null && (cleaf == null || cleaf.Hash != prevLeaf.Hash))
                {
                    byte[] prevData = prevLeaf.RawData;
                    if (prevData == null)
                    {
                        break;
                    }
                    var prevText = new RawText(prevData);
                    var differ   = new MyersDiff(prevText, curText);
                    foreach (Edit e in differ.getEdits())
                    {
                        for (int n = e.BeginB; n < e.EndB; n++)
                        {
                            if (lines [n] == null)
                            {
                                lines [n] = prevCommit;
                                emptyLines--;
                            }
                        }
                    }
                    if (cleaf == null || emptyLines <= 0)
                    {
                        break;
                    }
                }
                prevCommit = ancestor;
                prevLeaf   = cleaf;
            }
            for (int n = 0; n < lines.Length; n++)
            {
                if (lines [n] == null)
                {
                    lines [n] = prevAncestor;
                }
            }
            return(lines);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Creates a line-based diff from the the given byte arrays.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public Diff(byte[] a, byte[] b)
        {
            m_sequence_a = new Text(a);
            m_sequence_b = new Text(b);
            var diff = new MyersDiff((RawText)m_sequence_a, (RawText)m_sequence_b);             // <--- using the undocumented cast operator of Text

            m_edits = diff.getEdits();
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Makes comparison of two collections of string lines
        /// </summary>
        /// <param name="listA">Lines sequence A</param>
        /// <param name="listB">Lines sequence B</param>
        /// <returns>Edit script to transform A to B</returns>
        public EditScript Execute(IList <string> listA, IList <string> listB)
        {
            int[] hashA = HashStringList(listA);
            int[] hashB = HashStringList(listB);

            MyersDiff <int> diff   = new MyersDiff <int>(hashA, hashB, _supportChangeEditType);
            EditScript      result = diff.Execute();

            return(result);
        }
Ejemplo n.º 7
0
		public static IEnumerable<DiffEntry> CompareCommits (NGit.Repository repo, AnyObjectId reference, ObjectId compared)
		{
			var diff = new MyersDiff (repo);

			var firstTree = new CanonicalTreeParser ();
			firstTree.Reset (repo.NewObjectReader (), new RevWalk (repo).ParseTree (reference));
			diff.SetNewTree (firstTree);
			
			if (compared != ObjectId.ZeroId) {
				var secondTree = new CanonicalTreeParser ();
				secondTree.Reset (repo.NewObjectReader (), new RevWalk (repo).ParseTree (compared));

				if (compared != ObjectId.ZeroId)
					diff.SetOldTree (secondTree);
			}
			return diff.Call ();
		}
Ejemplo n.º 8
0
 /// <summary>
 /// Builds an EditScript which patches the original data to the desired data and uses this to create the Patchup patch.
 /// </summary>
 /// <param name="from">The original byte data which the patch will be applied to.</param>
 /// <param name="to">The desired byte data which will result after applying the patch to the original byte data.</param>
 public void Build(byte[] from, byte[] to)
 {
     this.EditScript = MyersDiff.SingleThreaded(from, to);
 }
Ejemplo n.º 9
0
        /// <summary>
        /// Does the three way merge between a common base and two sequences.
        /// </summary>
        /// <param name="base">base the common base sequence</param>
        /// <param name="ours">ours the first sequence to be merged</param>
        /// <param name="theirs">theirs the second sequence to be merged</param>
        /// <returns>the resulting content</returns>
        public static MergeResult merge(Sequence @base, Sequence ours,
                                        Sequence theirs)
        {
            List <Sequence> sequences = new List <Sequence>(3);

            sequences.Add(@base);
            sequences.Add(ours);
            sequences.Add(theirs);
            MergeResult         result       = new MergeResult(sequences);
            EditList            oursEdits    = new MyersDiff(@base, ours).getEdits();
            IteratorBase <Edit> baseToOurs   = oursEdits.iterator();
            EditList            theirsEdits  = new MyersDiff(@base, theirs).getEdits();
            IteratorBase <Edit> baseToTheirs = theirsEdits.iterator();
            int current = 0; // points to the next line (first line is 0) of base
            // which was not handled yet
            Edit oursEdit   = nextEdit(baseToOurs);
            Edit theirsEdit = nextEdit(baseToTheirs);

            // iterate over all edits from base to ours and from base to theirs
            // leave the loop when there are no edits more for ours or for theirs
            // (or both)
            while (theirsEdit != END_EDIT || oursEdit != END_EDIT)
            {
                if (oursEdit.EndA <= theirsEdit.BeginA)
                {
                    // something was changed in ours not overlapping with any change
                    // from theirs. First add the common part in front of the edit
                    // then the edit.
                    if (current != oursEdit.BeginA)
                    {
                        result.add(0, current, oursEdit.BeginA,
                                   MergeChunk.ConflictState.NO_CONFLICT);
                    }
                    result.add(1, oursEdit.BeginB, oursEdit.EndB,
                               MergeChunk.ConflictState.NO_CONFLICT);
                    current  = oursEdit.EndA;
                    oursEdit = nextEdit(baseToOurs);
                }
                else if (theirsEdit.EndA <= oursEdit.BeginA)
                {
                    // something was changed in theirs not overlapping with any
                    // from ours. First add the common part in front of the edit
                    // then the edit.
                    if (current != theirsEdit.BeginA)
                    {
                        result.add(0, current, theirsEdit.BeginA,
                                   MergeChunk.ConflictState.NO_CONFLICT);
                    }
                    result.add(2, theirsEdit.BeginB, theirsEdit.EndB,
                               MergeChunk.ConflictState.NO_CONFLICT);
                    current    = theirsEdit.EndA;
                    theirsEdit = nextEdit(baseToTheirs);
                }
                else
                {
                    // here we found a real overlapping modification

                    // if there is a common part in front of the conflict add it
                    if (oursEdit.BeginA != current &&
                        theirsEdit.BeginA != current)
                    {
                        result.add(0, current, Math.Min(oursEdit.BeginA,
                                                        theirsEdit.BeginA), MergeChunk.ConflictState.NO_CONFLICT);
                    }

                    // set some initial values for the ranges in A and B which we
                    // want to handle
                    int oursBeginB   = oursEdit.BeginB;
                    int theirsBeginB = theirsEdit.BeginB;
                    // harmonize the start of the ranges in A and B
                    if (oursEdit.BeginA < theirsEdit.BeginA)
                    {
                        theirsBeginB -= theirsEdit.BeginA
                                        - oursEdit.BeginA;
                    }
                    else
                    {
                        oursBeginB -= oursEdit.BeginA - theirsEdit.BeginA;
                    }

                    // combine edits:
                    // Maybe an Edit on one side corresponds to multiple Edits on
                    // the other side. Then we have to combine the Edits of the
                    // other side - so in the end we can merge together two single
                    // edits.
                    //
                    // It is important to notice that this combining will extend the
                    // ranges of our conflict always downwards (towards the end of
                    // the content). The starts of the conflicting ranges in ours
                    // and theirs are not touched here.
                    //
                    // This combining is an iterative process: after we have
                    // combined some edits we have to do the check again. The
                    // combined edits could now correspond to multiple edits on the
                    // other side.
                    //
                    // Example: when this combining algorithm works on the following
                    // edits
                    // oursEdits=((0-5,0-5),(6-8,6-8),(10-11,10-11)) and
                    // theirsEdits=((0-1,0-1),(2-3,2-3),(5-7,5-7))
                    // it will merge them into
                    // oursEdits=((0-8,0-8),(10-11,10-11)) and
                    // theirsEdits=((0-7,0-7))
                    //
                    // Since the only interesting thing to us is how in ours and
                    // theirs the end of the conflicting range is changing we let
                    // oursEdit and theirsEdit point to the last conflicting edit
                    Edit nextOursEdit   = nextEdit(baseToOurs);
                    Edit nextTheirsEdit = nextEdit(baseToTheirs);
                    for (; ;)
                    {
                        if (oursEdit.EndA > nextTheirsEdit.BeginA)
                        {
                            theirsEdit     = nextTheirsEdit;
                            nextTheirsEdit = nextEdit(baseToTheirs);
                        }
                        else if (theirsEdit.EndA > nextOursEdit.BeginA)
                        {
                            oursEdit     = nextOursEdit;
                            nextOursEdit = nextEdit(baseToOurs);
                        }
                        else
                        {
                            break;
                        }
                    }

                    // harmonize the end of the ranges in A and B
                    int oursEndB   = oursEdit.EndB;
                    int theirsEndB = theirsEdit.EndB;
                    if (oursEdit.EndA < theirsEdit.EndA)
                    {
                        oursEndB += theirsEdit.EndA - oursEdit.EndA;
                    }
                    else
                    {
                        theirsEndB += oursEdit.EndA - theirsEdit.EndA;
                    }

                    // Add the conflict
                    result.add(1, oursBeginB, oursEndB,
                               MergeChunk.ConflictState.FIRST_CONFLICTING_RANGE);
                    result.add(2, theirsBeginB, theirsEndB,
                               MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE);

                    current    = Math.Max(oursEdit.EndA, theirsEdit.EndA);
                    oursEdit   = nextOursEdit;
                    theirsEdit = nextTheirsEdit;
                }
            }
            // maybe we have a common part behind the last edit: copy it to the
            // result
            if (current < @base.size())
            {
                result.add(0, current, @base.size(), MergeChunk.ConflictState.NO_CONFLICT);
            }
            return(result);
        }
Ejemplo n.º 10
0
        public void assertDiff(string a, string b, string edits)
        {
            MyersDiff diff = new MyersDiff(toCharArray(a), toCharArray(b));

            Assert.AreEqual(edits, toString(diff.getEdits()));
        }