public static void WriteUnifiedDiff (string [] leftLines, string leftName, string [] rightLines, string rightName, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) { Diff diff = new Diff ( leftLines, rightLines, caseSensitive, compareWhitespace ); WriteUnifiedDiff (diff, writer, leftName, rightName, context); }
private static IDiff[] makediffs (IList original, IList [] changed, IComparer comparer, IHashCodeProvider hashcoder) { IDiff[] diffs = new IDiff[changed.Length]; for (int i = 0; i < changed.Length; i++) diffs [i] = new Diff (original, changed [i], comparer, hashcoder); return diffs; }
public static void WriteUnifiedDiff(Diff diff, TextWriter writer, string fromfile, string tofile, int context) { writer.Write("--- "); writer.WriteLine(fromfile); writer.Write("+++ "); writer.WriteLine(tofile); ArrayList hunkset = new ArrayList(); foreach (Diff.Hunk hunk in diff) { Diff.Hunk lasthunk = null; if (hunkset.Count > 0) lasthunk = (Diff.Hunk)hunkset[hunkset.Count - 1]; if (hunk.Same) { // At the start of a hunk set, keep only context lines of context. if (lasthunk == null) { if (hunk.Left.Count > context) hunkset.Add(hunk.Crop(hunk.Left.Count - context, 0)); else hunkset.Add(hunk); // Can't have two same hunks in a row, so the last one was a difference. } else { // Small enough context that this unified diff range will not stop. if (hunk.Left.Count <= context * 2) { hunkset.Add(hunk); // Too much of the same. Keep context lines and end this section. // And then keep the last context lines as context for the next section. } else { hunkset.Add(hunk.Crop(0, hunk.Left.Count - context)); WriteUnifiedDiffSection(writer, hunkset); hunkset.Clear(); if (hunk.Left.Count > context) hunkset.Add(hunk.Crop(hunk.Left.Count - context, 0)); else hunkset.Add(hunk); } } } else { hunkset.Add(hunk); } } if (hunkset.Count > 0 && !(hunkset.Count == 1 && ((Diff.Hunk)hunkset[0]).Same)) WriteUnifiedDiffSection(writer, hunkset); }
public void TestDeleteLines() { string[] leftLines = new string[] { "line1", "line2" }; string[] rightLines = new string[] { "line1" }; Diff diff = new Diff(leftLines, rightLines, true, true); Diff.Hunk[] hunks = Diff.GetHunkList(diff); Assert.That(hunks.Length, Is.EqualTo(2)); Assert.That(hunks[0].Left, Has.Count(1)); Assert.That(hunks[0].Right, Has.Count(1)); Assert.That(hunks[1].Left, Has.Count(1)); Assert.That(hunks[1].Right, Has.Count(0)); }
public Enumerator(Diff diff) { this.diff = diff; Reset(); }
internal Hunk (Merge merge, Diff.Hunk [][] hunks, int start, int count, bool same) { this.merge = merge; this.hunks = hunks; this.start = start; this.count = count; this.same = same; int ct = 0; foreach (Diff.Hunk[] hh in hunks) { foreach (Diff.Hunk h in hh) { if (!h.Same) { ct++; break; } } } conflict = (ct > 1); }
private float CompareLists(IList left, IList right, float threshold, bool output) { // Given two lists, find the elements in the list that correspond. // Two elements correspond if their 'difference metric' is less than // or equal to threshold. For the hunks of correspondent items, // recursively descend into items not truly equal. For hunks of // irreconsiliable material, raise the threshold to the next useful // level and rescan the items. if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); if (left.Count == 0 && right.Count == 0) return 0; NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this); Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this)); int nitems = 0, ndiffs = 0; foreach (Diff.Hunk hunk in diff) { if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) { // This comprises a block of one-to-one correspondent items who // differ by no more than the threshold value. nitems += hunk.Left.Count; bool inSameRegion = false; for (int i = 0; i < hunk.Left.Count; i++) { object oleft = hunk.Left[i]; object oright = hunk.Right[i]; NodeInterface ileft = GetInterface(oleft); NodeInterface iright = GetInterface(oright); IList cleft = null, cright = null; cleft = ileft.GetChildren(oleft); cright = iright.GetChildren(oright); float comp = 0; if (ileft == iright) comp = ileft.Compare(oleft, oright, this); // If the nodes are equal, emit one node. if (ileft == iright && comp == 0) { if (output) { if (!inSameRegion) { WritePushSame(); inSameRegion = true; } WriteNodeSame(ileft, oleft, oright); } // Recurse into the lists of each node. } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WritePushNode(ileft, oleft, oright); float d = CompareLists(cleft, cright, 0, output); d *= hunk.Left.Count; if (d < 1) d = 1; ndiffs += (int)d; if (output) WritePopNode(); // The nodes are not equal, so emit removed and added nodes. } else { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WriteNodeChange(ileft, oleft, iright, oright); ndiffs += hunk.Left.Count; } } if (output && inSameRegion) WritePopSame(); } else { // We have some arbitrary sets on the left and right side. // Try to match nodes on the left with nodes on the right. ArrayList leftitems = new ArrayList(hunk.Left); ArrayList rightitems = new ArrayList(hunk.Right); while (leftitems.Count > 0 && rightitems.Count > 0) { // Find the first node on the left side that matches // a node on the right side. int li = -1, ri = -1; float bestD = 1; for (int lii = 0; lii < leftitems.Count; lii++) { object oleft = leftitems[lii]; NodeInterface ileft = GetInterface(oleft); for (int rii = 0; rii < rightitems.Count; rii++) { object oright = rightitems[rii]; NodeInterface iright = GetInterface(oright); float d = 1; if (ileft == iright) d = ileft.Compare(oleft, oright, this); if (d < bestD) { bestD = d; li = lii; ri = rii; } } } if (li == -1) break; // Try to extend the correspondence further down the right list. int count = 1; for (int ri2 = ri+1; ri2 < rightitems.Count && (li+(ri2-ri)) < leftitems.Count; ri2++) { object oleft2 = leftitems[li+(ri2-ri)]; object oright2 = rightitems[ri2]; NodeInterface ileft2 = GetInterface(oleft2); NodeInterface iright2 = GetInterface(oright2); float d2 = 1; if (ileft2 == iright2) d2 = ileft2.Compare(oleft2, oright2, this); if (d2 <= bestD) count++; else break; } // We have a correspondence starting at li and ri // over count items. // First, recurse on the first portion of the list that had no matches. CompareLists(leftitems.GetRange(0, li), rightitems.GetRange(0, ri), comparer.minimumDifference, output); // Then recurse on the correspondence range. CompareLists(leftitems.GetRange(li, count), rightitems.GetRange(ri, count), comparer.minimumDifference, output); // Lastly remove the items we just considered and do it again for the rest of the hunk. leftitems.RemoveRange(0, li+count); rightitems.RemoveRange(0, ri+count); } int ct = leftitems.Count + rightitems.Count; nitems += ct; ndiffs += ct; if (output) { bool noRecurse = comparer.minimumDifference >= 1; if (rightitems.Count == 0 || (leftitems.Count > 0 && noRecurse)) WriteNodesRemoved(leftitems); if (leftitems.Count == 0 || (rightitems.Count > 0 && noRecurse)) WriteNodesAdded(rightitems); if (rightitems.Count != 0 && leftitems.Count != 0 && !noRecurse) CompareLists(leftitems, rightitems, comparer.minimumDifference, output); } } } return (float)ndiffs / (float)nitems; }
/// <summary> /// /// </summary> /// <param name="editorLeft"></param> /// <param name="editorRight"></param> /// <param name="fileBodyLeft"></param> /// <param name="fileBodyRight"></param> /// <returns>Returns true if files the same, false if they differ</returns> public static bool PerformDiff(ActiproSoftware.SyntaxEditor.SyntaxEditor editorLeft, ActiproSoftware.SyntaxEditor.SyntaxEditor editorRight, ref string fileBodyLeft, ref string fileBodyRight) { bool filesTheSame = true; editorLeft.Text = ""; editorRight.Text = ""; string file1 = fileBodyLeft; string file2 = fileBodyRight; // Performs a line-by-line, case-insensitive comparison. string[] arrFile1 = file1.Split('\n'); string[] arrFile2 = file2.Split('\n'); Diff diff = new Diff(arrFile1, arrFile2, true, false); foreach (Diff.Hunk hunk in diff) { int lineMismatch = hunk.Right.Count - hunk.Left.Count; Color leftCol; Color rightCol; bool iii = hunk.Same; if (!hunk.Same) { filesTheSame = false; } if (hunk.Same) { leftCol = Color.White; rightCol = Color.White; } else if (lineMismatch < 0) { leftCol = ColourNewGen; rightCol = Color.Silver; } else { leftCol = Color.Silver; rightCol = leftCol = ColourUser; } for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { string leftLine = arrFile1[i]; if (!hunk.Same) { leftCol = ColourNewGen; editorLeft.Document.AppendText(arrFile1[i]); if (editorLeft.Document.Lines.Count > 1) { editorLeft.Document.Lines[editorLeft.Document.Lines.Count - 2].BackColor = leftCol; } } else { editorLeft.Document.AppendText(arrFile1[i]); if (editorLeft.Document.Lines.Count > 1) { editorLeft.Document.Lines[editorLeft.Document.Lines.Count - 2].BackColor = leftCol; } } } for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { string rightLine = arrFile2[i]; if (!hunk.Same) { leftCol = ColourUser; editorRight.Document.AppendText(arrFile2[i]); if (editorRight.Document.Lines.Count > 2) { editorRight.Document.Lines[editorRight.Document.Lines.Count - 2].BackColor = leftCol; } } else { editorRight.Document.AppendText(arrFile2[i]); if (editorRight.Document.Lines.Count > 1) { editorRight.Document.Lines[editorRight.Document.Lines.Count - 2].BackColor = rightCol; } } } if (hunk.Left.Count < hunk.Right.Count) { for (int i = 0; i < hunk.Right.Count - hunk.Left.Count; i++) { editorLeft.Document.AppendText("" + Environment.NewLine); editorLeft.Document.Lines[editorLeft.Document.Lines.Count - 2].BackColor = Color.Silver; } } if (hunk.Right.Count < hunk.Left.Count) { for (int i = 0; i < hunk.Left.Count - hunk.Right.Count; i++) { editorRight.Document.AppendText("" + Environment.NewLine); editorRight.Document.Lines[editorRight.Document.Lines.Count - 2].BackColor = Color.Silver; } } } return filesTheSame; }
private void ProcessConflict(Merge.Conflict conflict) { string[] userLines = SlyceMerge.GetLinesFromRange(user, conflict.Ranges[Merge.UserIndex]); string[] newgenLines = SlyceMerge.GetLinesFromRange(newgen, conflict.Ranges[Merge.NewGenIndex]); if (userLines.Length == 0 && newgenLines.Length == 0) { // user and template have both deleted some lines. Add them in as virtual lines. foreach (string line in SlyceMerge.GetLinesFromRange(prevgen, conflict.Ranges[Merge.PrevGenIndex])) { vdo.AddLineToLeft(line, ChangeType.UserAndTemplate); } return; } Diff diff = new Diff(userLines, newgenLines, true, true); foreach (Diff.Hunk hunk in diff) { if (hunk.Same) { foreach (string line in hunk.Original()) { vdo.AddLine(line, ChangeType.None); } continue; } // Conflict VisualDiffOutput.ConflictRange crange = new VisualDiffOutput.ConflictRange(); crange.StartLineIndex = vdo.LeftLines.Count; for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { vdo.LeftLines.Add(new DiffLine(userLines[i], ChangeType.User)); vdo.RightLines.Add(new DiffLine("", ChangeType.User, true)); } for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { vdo.LeftLines.Add(new DiffLine(newgenLines[i], ChangeType.Template)); vdo.RightLines.Add(new DiffLine("", ChangeType.Template, true)); } crange.EndLineIndex = vdo.LeftLines.Count; vdo.ConflictRanges.Add(crange); vdo.DiffType = TypeOfDiff.Conflict; } }
private float CompareLists(IList left, IList right, float threshold, bool output) { // Given two lists, find the elements in the list that correspond. // Two elements correspond if their 'difference metric' is less than // or equal to threshold. For the hunks of correspondent items, // recursively descend into items not truly equal. For hunks of // irreconsiliable material, raise the threshold to the next useful // level and rescan the items. if (left.Count == 0 && right.Count == 0) return 0; NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this); Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this)); int nitems = 0, ndiffs = 0; foreach (Diff.Hunk hunk in diff) { if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) { // This comprises a block of correspondent items who // differ by no more than the threshold value. nitems += hunk.Left.Count; bool inSameRegion = false; for (int i = 0; i < hunk.Left.Count; i++) { object oleft = hunk.Left[i]; object oright = hunk.Right[i]; NodeInterface ileft = GetInterface(oleft); NodeInterface iright = GetInterface(oright); IList cleft = null, cright = null; cleft = ileft.GetChildren(oleft); cright = iright.GetChildren(oright); float comp = 0; if (ileft == iright) comp = ileft.Compare(oleft, oright, this); // If the nodes are equal, emit one node. if (ileft == iright && comp == 0) { if (output) { if (!inSameRegion) { WritePushSame(); inSameRegion = true; } WriteNodeSame(ileft, oleft, oright); } // Recurse into the lists of each node. } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WritePushNode(ileft, oleft, oright); float d = CompareLists(cleft, cright, 0, output); d *= hunk.Left.Count; if (d < 1) d = 1; ndiffs += (int)d; if (output) WritePopNode(); // The nodes are not equal, so emit removed and added nodes. } else { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WriteNodeChange(ileft, oleft, iright, oright); ndiffs += hunk.Left.Count; } } if (output && inSameRegion) WritePopSame(); } else { int ct = hunk.Left.Count + hunk.Right.Count; nitems += ct; ndiffs += ct; if (output) { bool noRecurse = comparer.minimumDifference >= 1; if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse)) WriteNodesRemoved(hunk.Left); if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse)) WriteNodesAdded(hunk.Right); if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse) CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output); } } } return (float)ndiffs / (float)nitems; }
internal void MergeOutput(VisualDiffOutput otherOutput, ChangeType thisChangeType, ChangeType otherChangeType) { string[] thisTempLines = LinesToStringArray(this.RightLines); string[] otherTempLines = LinesToStringArray(otherOutput.RightLines); this.LeftLines.Clear(); this.RightLines.Clear(); Diff diff = new Diff(thisTempLines, otherTempLines, true, true); // loop through parts of the diff foreach (Diff.Hunk hunk in diff) { int lineMismatch = hunk.Right.Start - hunk.Left.Start; if (hunk.Same) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { if (otherOutput.RightLines[i].Change != ChangeType.None) { //Both the user and template have changed to the same thing. Need to display this. leftLines.Add(new DiffLine(otherOutput.LeftLines[i].Text, thisChangeType | otherChangeType, otherOutput.LeftLines[i].IsVirtual)); rightLines.Add(new DiffLine(otherTempLines[i], thisChangeType | otherChangeType)); } else { leftLines.Add(new DiffLine(otherTempLines[i])); rightLines.Add(new DiffLine(otherTempLines[i])); } } } else { // If the left and right of the hunk both have lines, this could be a conflict. if (hunk.Right.Count != 0 && hunk.Left.Count != 0) { ConflictRange conflictRange = new ConflictRange(); conflictRange.StartLineIndex = leftLines.Count; for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { leftLines.Add(new DiffLine(otherTempLines[i], otherChangeType)); rightLines.Add(new DiffLine("", otherChangeType, true)); } for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { leftLines.Add(new DiffLine(thisTempLines[i], thisChangeType)); rightLines.Add(new DiffLine("", thisChangeType, true)); } conflictRange.EndLineIndex = leftLines.Count; conflictRanges.Add(conflictRange); } else if (hunk.Right.Count != 0) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { leftLines.Add(new DiffLine("", otherChangeType, true)); rightLines.Add(new DiffLine(otherTempLines[i], otherChangeType)); } } else { for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { leftLines.Add(new DiffLine("", thisChangeType, true)); rightLines.Add(new DiffLine(thisTempLines[i], thisChangeType)); } } } } }
/// <summary> /// Perform 3-way merge /// </summary> /// <returns>Merged text.</returns> /// <param name="merge"></param> /// <param name="prevGenLines">Previously generated version ie: 'base version'.</param> /// <param name="userLines">User-modified version of 'base'.</param> /// <param name="nextGenLines">Latest generated version of 'base'.</param> internal static string GetMergedOutput(SlyceMergeResult merge, string[] prevGenLines, string[] userLines, string[] nextGenLines) { // default values and initialisation merge.DiffType = TypeOfDiff.ExactCopy; merge.Lines = new List<LineText>(200); string output = ""; int lineCounter = -1; const string pilcrowString = "¶"; const char pilcrow = '¶'; // diff the User version and LatestGenerated version against the PrevGenerated version IList res = Merge.MergeLists(prevGenLines, new[] { userLines, nextGenLines }); // handle empty input if (res.Count == 1 && res[0].Equals(string.Empty)) { return string.Empty; } string conflictTypeName = typeof(Merge.Conflict).FullName; // process each line from the diff foreach (object line in res) { lineCounter++; string lineTypeName = line.GetType().ToString(); if (lineTypeName == "System.String") { string thisLine = (string)line; merge.Lines.Add(new LineText(thisLine.Replace(pilcrowString, ""), lineCounter, TypeOfLine.Normal)); } else if (lineTypeName == conflictTypeName) { Merge.Conflict conf = (Merge.Conflict)line; Range[] ranges = conf.Ranges; Range rangeUser = ranges[0]; Range rangeNewGen = ranges[1]; string[] conflictUserLines = GetLinesFromRange(userLines, rangeUser); string[] conflictNewGenLines = GetLinesFromRange(nextGenLines, rangeNewGen); // Get diff of the conflicts Diff diff = new Diff(conflictUserLines, conflictNewGenLines, true, false); foreach (Diff.Hunk hunk in diff) { if (hunk.Same) { string same = GetPortionOfString(conflictUserLines, hunk.Left.Start, hunk.Left.End); same = RemoveTrailingCharacter(pilcrowString, same); same = same.Replace(pilcrowString, "\r\n"); output += same; merge.Lines.Add(new LineText(same, lineCounter, TypeOfLine.Normal)); } else { // Get the user modified lines string userPortion = GetPortionOfString(conflictUserLines, hunk.Left.Start, hunk.Left.End); userPortion = RemoveTrailingCharacter(pilcrowString, userPortion); // Get the newly generated lines string newGenPortion = GetPortionOfString(conflictNewGenLines, hunk.Right.Start, hunk.Right.End); newGenPortion = RemoveTrailingCharacter(pilcrowString, newGenPortion); merge.SetDiffType(TypeOfDiff.Conflict); TypeOfLine lineType = newGenPortion.Length > 0 ? TypeOfLine.User : TypeOfLine.Normal; string[] userSplitLines = userPortion.Split(pilcrow); if (userPortion.Length > 0) { merge.Lines.Add(new LineText(userSplitLines[0], lineCounter, lineType)); } for (int myCount = 1; myCount < userSplitLines.Length; myCount++) { lineCounter++; merge.Lines.Add(new LineText(userSplitLines[myCount], lineCounter, lineType)); } lineType = userPortion.Length > 0 ? TypeOfLine.NewGen : TypeOfLine.Normal; string[] newGenSplitLines = newGenPortion.Split(pilcrow); if (newGenPortion.Length > 0) { merge.Lines.Add(new LineText(newGenSplitLines[0], lineCounter, lineType)); } for (int myCount = 1; myCount < newGenSplitLines.Length; myCount++) { lineCounter++; merge.Lines.Add(new LineText(newGenSplitLines[myCount], lineCounter, lineType)); } } } } else { throw new Exception(string.Format("Unexpected line type: {0}\nText1:{1}\n\nText2:{2}\n\nText3:{3}", line.GetType(), prevGenLines, userLines, nextGenLines)); } } return output; }
/// <summary> /// Merge two text files, by performing a line-by-line, case-sensitive comparison /// </summary> /// <returns>Enum indicating Conflict, Warning or Exact Copy</returns> /// <param name="identifyConflictsOnly">Flag - if true, don't merge</param> /// <param name="fileBodyLeft">Contents of LEFT file</param> /// <param name="fileBodyRight">Contents of RIGHT file</param> /// <param name="leftConflictLines">Array of lines with conflict from LEFT file</param> /// <param name="rightConflictLines">Array of lines with conflict from RIGHT file</param> /// <param name="combinedText">Result of merging files.</param> public static TypeOfDiff PerformTwoWayDiff( bool identifyConflictsOnly, string fileBodyLeft, string fileBodyRight, out LineSpan[] leftConflictLines, out LineSpan[] rightConflictLines, out string combinedText) { TypeOfDiff returnValue = TypeOfDiff.ExactCopy; combinedText = ""; StringUtility.RemoveTrailingLineBreaks(ref fileBodyLeft); StringUtility.RemoveTrailingLineBreaks(ref fileBodyRight); // break files into arrays of lines string[] leftLines = Common.Utility.StandardizeLineBreaks(fileBodyLeft, Common.Utility.LineBreaks.Unix).Split('\n'); string[] rightLines = Common.Utility.StandardizeLineBreaks(fileBodyRight, Common.Utility.LineBreaks.Unix).Split('\n'); // handle case where at least one file is empty if (fileBodyLeft.Length == 0 || fileBodyRight.Length == 0) { // both files are empty (unlikely in practice) if (fileBodyLeft.Length == 0 && fileBodyRight.Length == 0) { leftConflictLines = new LineSpan[0]; rightConflictLines = new LineSpan[0]; return TypeOfDiff.ExactCopy; } // one file is empty if (fileBodyLeft.Length > 0) { combinedText = fileBodyLeft; leftConflictLines = new[] { new LineSpan(0, leftLines.Length - 1) }; rightConflictLines = new LineSpan[0]; } else { combinedText = fileBodyRight; leftConflictLines = new LineSpan[0]; rightConflictLines = new[] { new LineSpan(0, rightLines.Length - 1) }; } return TypeOfDiff.Warning; } // initialise variables for merging StringBuilder sbMerged = new StringBuilder(Math.Max(fileBodyLeft.Length, fileBodyRight.Length) + 1000); // DMW_Question Is a 'combinedLineCount' really a 'mergedLineCount'? int combinedLineCount = 0; ArrayList combinedLeftColouredLines = new ArrayList(); ArrayList combinedRightColouredLines = new ArrayList(); // perform the diff (case-sensitive, check white space) Diff diff = new Diff(leftLines, rightLines, true, true); // loop through parts of the diff foreach (Diff.Hunk hunk in diff) { if (hunk.Same) { for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { //DMW_Changed sbMerged.Append(leftLines[i] + Environment.NewLine); sbMerged.AppendLine(leftLines[i]); combinedLineCount++; } } else // hunks are different { if (hunk.Left.Count > 0 && hunk.Right.Count > 0) { returnValue = TypeOfDiff.Conflict; } else if (returnValue != TypeOfDiff.Conflict) { returnValue = TypeOfDiff.Warning; } // LEFT file for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { //DMW_Changed sbMerged.Append(leftLines[i] + Environment.NewLine); sbMerged.AppendLine(leftLines[i]); if (!identifyConflictsOnly || (hunk.Left.Count > 0 && hunk.Right.Count > 0)) { combinedLeftColouredLines.Add(new LineSpan(combinedLineCount, combinedLineCount)); } combinedLineCount++; } // RIGHT file for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { //DMW_Changed sbMerged.Append(leftLines[i] + Environment.NewLine); sbMerged.AppendLine(rightLines[i]); if (!identifyConflictsOnly || (hunk.Left.Count > 0 && hunk.Right.Count > 0)) { combinedRightColouredLines.Add(new LineSpan(combinedLineCount, combinedLineCount)); } combinedLineCount++; } } } leftConflictLines = (LineSpan[])combinedLeftColouredLines.ToArray(typeof(LineSpan)); rightConflictLines = (LineSpan[])combinedRightColouredLines.ToArray(typeof(LineSpan)); combinedText = sbMerged.ToString(); return returnValue; }
/// <summary> /// Gets whether two text files are the same or not. Linebreaks get standardized inside this function. /// </summary> /// <param name="fileBodyLeft">LEFT text.</param> /// <param name="fileBodyRight">RIGHT text.</param> /// <param name="caseSensitive">True to consider case, false to ignore differences due to case.</param> /// <param name="compareWhitespace">True to consider whitespace, false to ignore differences in whitespace.</param> public static bool FilesAreTheSame(string fileBodyLeft, string fileBodyRight, bool caseSensitive, bool compareWhitespace) { StringUtility.RemoveTrailingLineBreaks(ref fileBodyLeft); StringUtility.RemoveTrailingLineBreaks(ref fileBodyRight); // Performs a line-by-line, case-insensitive comparison. string[] arrFile1 = Common.Utility.StandardizeLineBreaks(fileBodyLeft, Common.Utility.LineBreaks.Unix).Split('\n'); string[] arrFile2 = Common.Utility.StandardizeLineBreaks(fileBodyRight, Common.Utility.LineBreaks.Unix).Split('\n'); Diff diff = new Diff(arrFile1, arrFile2, caseSensitive, compareWhitespace); foreach (Diff.Hunk hunk in diff) { if (!hunk.Same) { return false; } } return true; }
public VisualDiffOutput ProcessMergeOutput() { if (fileInformation.UserFile.HasContents == false) throw new InvalidOperationException("Cannot merge: User file has no contents"); if (fileInformation.NewGenFile.HasContents == false) throw new InvalidOperationException("Cannot merge: Template file has no contents"); VisualDiffOutput vdo = new VisualDiffOutput(); string leftText = Common.Utility.StandardizeLineBreaks(fileInformation.NewGenFile.GetContents(), Common.Utility.LineBreaks.Unix); string rightText = Common.Utility.StandardizeLineBreaks(fileInformation.UserFile.GetContents(), Common.Utility.LineBreaks.Unix); StringUtility.RemoveTrailingLineBreaks(ref leftText); StringUtility.RemoveTrailingLineBreaks(ref rightText); leftText = Common.Utility.StandardizeLineBreaks(leftText, Common.Utility.LineBreaks.Unix); rightText = Common.Utility.StandardizeLineBreaks(rightText, Common.Utility.LineBreaks.Unix); string[] leftLines = leftText.Split('\n'); string[] rightLines = rightText.Split('\n'); Diff diff = new Diff(leftLines, rightLines, true, true); foreach(Diff.Hunk h in diff) { if(h.Same) { foreach(string line in h.Original()) { vdo.AddLine(line, ChangeType.None); } continue; } // Is this a conflict? if(h.Left.Count > 0 && h.Right.Count > 0) { int startIndex = vdo.LineCount; foreach (string line in h.Right) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.User)); } foreach (string line in h.Left) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.Template)); } for (int i = 0; i < h.Right.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } for (int i = 0; i < h.Left.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(startIndex, vdo.LineCount)); continue; } // Not a conflict. Just add the new lines and put virtual lines on the left. // Only one of these will actually run - we've already handled the case where // both sides of the hunk have lines. foreach(string line in h.Left) { vdo.RightLines.Add(new DiffLine(line, ChangeType.Template)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } foreach (string line in h.Right) { vdo.RightLines.Add(new DiffLine(line, ChangeType.User)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } } if(vdo.LineCount > 0) vdo.RemoveLine(vdo.LineCount - 1); return vdo; }
public static Hunk[] GetHunkList(Diff diff) { List<Hunk> hunks = new List<Hunk>(); foreach(Hunk hunk in diff) { hunks.Add(hunk); } return hunks.ToArray(); }
public static void WriteUnifiedDiff (Diff diff, TextWriter writer) { WriteUnifiedDiff (diff, writer, "Left", "Right", 2); }
private static void ProcessSingleChange(FileInformation<string> fileInfo, VisualDiffOutput output, string[] newlines, ChangeType changeType) { string[] prevGenLines = Common.Utility.StandardizeLineBreaks(fileInfo.PrevGenFile.GetContents(), Common.Utility.LineBreaks.Unix).Split('\n'); Diff diff = new Diff(prevGenLines, newlines, true, true); // loop through parts of the diff foreach (Diff.Hunk hunk in diff) { if (hunk.Same) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.LeftLines.Add(new DiffLine(newlines[i])); output.RightLines.Add(new DiffLine(newlines[i])); } } else { // If the left and right of the hunk both have lines in the hunk, this is a change. if (hunk.Right.Count != 0 && hunk.Left.Count != 0) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.RightLines.Add(new DiffLine(newlines[i], changeType)); } for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { output.LeftLines.Add(new DiffLine(prevGenLines[i], ChangeType.None)); } while (output.LeftLines.Count < output.RightLines.Count) { output.LeftLines.Add(new DiffLine("", changeType, true)); } while (output.RightLines.Count < output.LeftLines.Count) { output.RightLines.Add(new DiffLine("", changeType, true)); } } else if (hunk.Right.Count != 0) // The right hand side has added lines. { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.LeftLines.Add(new DiffLine("", changeType, true)); output.RightLines.Add(new DiffLine(newlines[i], changeType)); } } else // The left hand side has extra lines. { for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { output.LeftLines.Add(new DiffLine(prevGenLines[i], ChangeType.None)); output.RightLines.Add(new DiffLine("", changeType, true)); } } } } }