/// <summary> /// This method creates a unified diff file. /// </summary> /// <param name="oldInfo">Provides the information for the old file (optional).</param> /// <param name="newInfo">Provides the information for the new file (optional).</param> /// <param name="streamOut">Stream where the diff file is written.</param> /// <param name="contextLines">Number of context lines around hunks.</param> /// <returns></returns> public static bool Create(UnifiedDiffInfo oldInfo, UnifiedDiffInfo newInfo, Stream streamOut, int contextLines) { if (streamOut == null) throw new ArgumentException("streamOut must not be null"); if (contextLines < 0) throw new ArgumentException("contextLines cannot be negative"); StreamReader streamReader1 = new StreamReader(oldInfo != null && oldInfo.Stream != null ? oldInfo.Stream : new MemoryStream()); StreamReader streamReader2 = new StreamReader(newInfo != null && newInfo.Stream != null ? newInfo.Stream : new MemoryStream()); StreamWriter streamWriter = new StreamWriter(streamOut); List<DiffLine> lines1 = new List<DiffLine>(); List<DiffLine> lines2 = new List<DiffLine>(); string line1, line2; int start = 0, end = 0, firstLine = 0; // Read text stream into a list. If the beginning of the file matches, this only keeps the // the last contextLines number of lines in memory for the unified context of the // first hunk. Diff.CreateDiff() actually would do this for us too, but we don't need // to waste a lot of memory if we're not going to use it anyway. do { line1 = streamReader1.ReadLine(); line2 = streamReader2.ReadLine(); if (line1 != null && line2 != null && string.Equals(line1, line2)) { lines1.Add(new DiffLine(firstLine, line1)); lines2.Add(new DiffLine(firstLine, line2)); if (start == contextLines) { lines1.RemoveAt(0); lines2.RemoveAt(0); firstLine++; } else start++; continue; } while (line1 != null) { lines1.Add(new DiffLine(firstLine + lines1.Count, line1)); line1 = streamReader1.ReadLine(); } while (line2 != null) { lines2.Add(new DiffLine(firstLine + lines2.Count, line2)); line2 = streamReader2.ReadLine(); } } while (line1 != null || line2 != null); // Also get rid of the last lines of the file if they are equal int endCnt = Math.Min(lines1.Count, lines2.Count) - start; int removeEnd = 0; for (int i = 0; i < endCnt; i++) { if (string.Equals(lines1[lines1.Count - i - 1].line, lines2[lines2.Count - i - 1].line)) { if (end == contextLines) removeEnd++; else end++; } else break; } if (removeEnd > 0) { lines1.RemoveRange(lines1.Count - removeEnd, removeEnd); lines2.RemoveRange(lines2.Count - removeEnd, removeEnd); } // Now we've got a list of lines to compare. The list context contains start // lines before the first difference, and end lines after the last difference. List<DiffEntry<DiffLine>> diffEntries = Diff.CreateDiff<DiffLine>(lines1, lines2, start, end); // Create a list of hunks and their lines including the context lines List<Hunk> hunks = new List<Hunk>(); Hunk hunk = null; int l1 = firstLine, l2 = firstLine; for (int i = 0; i < diffEntries.Count; i++) { DiffEntry<DiffLine> entry = diffEntries[i]; if (hunk == null) { if (entry.EntryType == DiffEntry<DiffLine>.DiffEntryType.Equal) { int cnt = Math.Min(entry.Count, contextLines); hunk = new Hunk(l1 + entry.Count - cnt, l2 + entry.Count - cnt); for (int j = cnt; j > 0; j--) hunk.AddContext(lines1[l1 + entry.Count - j - firstLine].line); hunks.Add(hunk); l1 += entry.Count; l2 += entry.Count; continue; } hunk = new Hunk(l1, l2); hunks.Add(hunk); } switch (entry.EntryType) { case DiffEntry<DiffLine>.DiffEntryType.Add: hunk.AddNew(lines2[l2 - firstLine].line); l2++; break; case DiffEntry<DiffLine>.DiffEntryType.Remove: hunk.AddRemove(lines1[l1 - firstLine].line); l1++; break; case DiffEntry<DiffLine>.DiffEntryType.Equal: if (i == diffEntries.Count - 1) { int cnt = Math.Min(contextLines, entry.Count); for (int j = 0; j < cnt; j++) hunk.AddContext(lines1[l1 + j - firstLine].line); } else { if (entry.Count > 2 * contextLines) { for (int j = 0; j < contextLines; j++) hunk.AddContext(lines1[l1 + j - firstLine].line); l1 += entry.Count; l2 += entry.Count; hunk = new Hunk(l1 - contextLines, l2 - contextLines); for (int j = contextLines; j > 0; j--) hunk.AddContext(lines1[l1 - j - firstLine].line); hunks.Add(hunk); } else { for (int j = 0; j < entry.Count; j++) hunk.AddContext(lines1[l1 + j - firstLine].line); l1 += entry.Count; l2 += entry.Count; } } break; } } if (hunks.Count > 0) { // Write the hunks to the output stream if (oldInfo != null && !string.IsNullOrEmpty(oldInfo.Label)) streamWriter.WriteLine(string.Format("--- {0}", oldInfo.Label)); if (newInfo != null && !string.IsNullOrEmpty(newInfo.Label)) streamWriter.WriteLine(string.Format("+++ {0}", newInfo.Label)); foreach (var hk in hunks) hk.Write(streamWriter); streamWriter.WriteLine(); streamWriter.Flush(); return true; } return false; }
/// <summary> /// This method creates a unified diff file. /// </summary> /// <param name="oldInfo">Provides the information for the old file (optional).</param> /// <param name="newInfo">Provides the information for the new file (optional).</param> /// <param name="streamOut">Stream where the diff file is written.</param> /// <param name="contextLines">Number of context lines around hunks.</param> /// <returns></returns> public static bool Create(UnifiedDiffInfo oldInfo, UnifiedDiffInfo newInfo, Stream streamOut, int contextLines) { if (streamOut == null) { throw new ArgumentException("streamOut must not be null"); } if (contextLines < 0) { throw new ArgumentException("contextLines cannot be negative"); } StreamReader streamReader1 = new StreamReader(oldInfo != null && oldInfo.Stream != null ? oldInfo.Stream : new MemoryStream()); StreamReader streamReader2 = new StreamReader(newInfo != null && newInfo.Stream != null ? newInfo.Stream : new MemoryStream()); StreamWriter streamWriter = new StreamWriter(streamOut); List <DiffLine> lines1 = new List <DiffLine>(); List <DiffLine> lines2 = new List <DiffLine>(); string line1, line2; int start = 0, end = 0, firstLine = 0; // Read text stream into a list. If the beginning of the file matches, this only keeps the // the last contextLines number of lines in memory for the unified context of the // first hunk. Diff.CreateDiff() actually would do this for us too, but we don't need // to waste a lot of memory if we're not going to use it anyway. do { line1 = streamReader1.ReadLine(); line2 = streamReader2.ReadLine(); if (line1 != null && line2 != null && string.Equals(line1, line2)) { lines1.Add(new DiffLine(firstLine, line1)); lines2.Add(new DiffLine(firstLine, line2)); if (start == contextLines) { lines1.RemoveAt(0); lines2.RemoveAt(0); firstLine++; } else { start++; } continue; } while (line1 != null) { lines1.Add(new DiffLine(firstLine + lines1.Count, line1)); line1 = streamReader1.ReadLine(); } while (line2 != null) { lines2.Add(new DiffLine(firstLine + lines2.Count, line2)); line2 = streamReader2.ReadLine(); } } while (line1 != null || line2 != null); // Also get rid of the last lines of the file if they are equal int endCnt = Math.Min(lines1.Count, lines2.Count) - start; int removeEnd = 0; for (int i = 0; i < endCnt; i++) { if (string.Equals(lines1[lines1.Count - i - 1].line, lines2[lines2.Count - i - 1].line)) { if (end == contextLines) { removeEnd++; } else { end++; } } else { break; } } if (removeEnd > 0) { lines1.RemoveRange(lines1.Count - removeEnd, removeEnd); lines2.RemoveRange(lines2.Count - removeEnd, removeEnd); } // Now we've got a list of lines to compare. The list context contains start // lines before the first difference, and end lines after the last difference. List <DiffEntry <DiffLine> > diffEntries = Diff.CreateDiff <DiffLine>(lines1, lines2, start, end); // Create a list of hunks and their lines including the context lines List <Hunk> hunks = new List <Hunk>(); Hunk hunk = null; int l1 = firstLine, l2 = firstLine; for (int i = 0; i < diffEntries.Count; i++) { DiffEntry <DiffLine> entry = diffEntries[i]; if (hunk == null) { if (entry.EntryType == DiffEntry <DiffLine> .DiffEntryType.Equal) { int cnt = Math.Min(entry.Count, contextLines); hunk = new Hunk(l1 + entry.Count - cnt, l2 + entry.Count - cnt); for (int j = cnt; j > 0; j--) { hunk.AddContext(lines1[l1 + entry.Count - j - firstLine].line); } hunks.Add(hunk); l1 += entry.Count; l2 += entry.Count; continue; } hunk = new Hunk(l1, l2); hunks.Add(hunk); } switch (entry.EntryType) { case DiffEntry <DiffLine> .DiffEntryType.Add: hunk.AddNew(lines2[l2 - firstLine].line); l2++; break; case DiffEntry <DiffLine> .DiffEntryType.Remove: hunk.AddRemove(lines1[l1 - firstLine].line); l1++; break; case DiffEntry <DiffLine> .DiffEntryType.Equal: if (i == diffEntries.Count - 1) { int cnt = Math.Min(contextLines, entry.Count); for (int j = 0; j < cnt; j++) { hunk.AddContext(lines1[l1 + j - firstLine].line); } } else { if (entry.Count > 2 * contextLines) { for (int j = 0; j < contextLines; j++) { hunk.AddContext(lines1[l1 + j - firstLine].line); } l1 += entry.Count; l2 += entry.Count; hunk = new Hunk(l1 - contextLines, l2 - contextLines); for (int j = contextLines; j > 0; j--) { hunk.AddContext(lines1[l1 - j - firstLine].line); } hunks.Add(hunk); } else { for (int j = 0; j < entry.Count; j++) { hunk.AddContext(lines1[l1 + j - firstLine].line); } l1 += entry.Count; l2 += entry.Count; } } break; } } if (hunks.Count > 0) { // Write the hunks to the output stream if (oldInfo != null && !string.IsNullOrEmpty(oldInfo.Label)) { streamWriter.WriteLine(string.Format("--- {0}", oldInfo.Label)); } if (newInfo != null && !string.IsNullOrEmpty(newInfo.Label)) { streamWriter.WriteLine(string.Format("+++ {0}", newInfo.Label)); } foreach (var hk in hunks) { hk.Write(streamWriter); } streamWriter.WriteLine(); streamWriter.Flush(); return(true); } return(false); }