public static List <Patch3Set> Diff3MergeIndices(string[] a, string[] o, string[] b) { // Given three files, A, O, and B, where both A and B are // independently derived from O, returns a fairly complicated // internal representation of merge decisions it's taken. The // interested reader may wish to consult // // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A // Formal Investigation of Diff3." In Arvind and Prasad, // editors, Foundations of Software Technology and Theoretical // Computer Science (FSTTCS), December 2007. // // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf) var m1 = Diff.DiffIndices(o, a); var m2 = Diff.DiffIndices(o, b); var hunks = new List <Diff3Set>(); for (int i = 0; i < m1.Count; i++) { AddHunk(m1[i], Side.Left, hunks); } for (int i = 0; i < m2.Count; i++) { AddHunk(m2[i], Side.Right, hunks); } hunks.Sort(); var result = new List <Patch3Set>(); var commonOffset = 0; for (var hunkIndex = 0; hunkIndex < hunks.Count; hunkIndex++) { var firstHunkIndex = hunkIndex; var hunk = hunks[hunkIndex]; var regionLhs = hunk.File1Offset; var regionRhs = regionLhs + hunk.File1Length; while (hunkIndex < hunks.Count - 1) { var maybeOverlapping = hunks[hunkIndex + 1]; var maybeLhs = maybeOverlapping.File1Offset; if (maybeLhs > regionRhs) { break; } regionRhs = Math.Max(regionRhs, maybeLhs + maybeOverlapping.File1Length); hunkIndex++; } CopyCommon2(regionLhs, ref commonOffset, result); if (firstHunkIndex == hunkIndex) { // The "overlap" was only one hunk long, meaning that // there's no conflict here. Either a and o were the // same, or b and o were the same. if (hunk.File2Length > 0) { result.Add(new Patch3Set { Side = hunk.Side, Offset = hunk.File2Offset, Length = hunk.File2Length }); } } else { // A proper conflict. Determine the extents of the // regions involved from a, o and b. Effectively merge // all the hunks on the left into one giant hunk, and // do the same for the right; then, correct for skew // in the regions of o that each Side changed, and // report appropriate spans for the three sides. var regions = new Dictionary <Side, ConflictRegion> { { Side.Left, new ConflictRegion { File1RegionStart = a.Length, File1RegionEnd = -1, File2RegionStart = o.Length, File2RegionEnd = -1 } }, { Side.Right, new ConflictRegion { File1RegionStart = b.Length, File1RegionEnd = -1, File2RegionStart = o.Length, File2RegionEnd = -1 } } }; for (int i = firstHunkIndex; i <= hunkIndex; i++) { hunk = hunks[i]; var side = hunk.Side; var r = regions[side]; var oLhs = hunk.File1Offset; var oRhs = oLhs + hunk.File1Length; var abLhs = hunk.File2Offset; var abRhs = abLhs + hunk.File2Length; r.File1RegionStart = Math.Min(abLhs, r.File1RegionStart); r.File1RegionEnd = Math.Max(abRhs, r.File1RegionEnd); r.File2RegionStart = Math.Min(oLhs, r.File2RegionStart); r.File2RegionEnd = Math.Max(oRhs, r.File2RegionEnd); } var aLhs = regions[Side.Left].File1RegionStart + (regionLhs - regions[Side.Left].File2RegionStart); var aRhs = regions[Side.Left].File1RegionEnd + (regionRhs - regions[Side.Left].File2RegionEnd); var bLhs = regions[Side.Right].File1RegionStart + (regionLhs - regions[Side.Right].File2RegionStart); var bRhs = regions[Side.Right].File1RegionEnd + (regionRhs - regions[Side.Right].File2RegionEnd); result.Add(new Patch3Set { Side = Side.Conflict, Offset = aLhs, Length = aRhs - aLhs, ConflictOldOffset = regionLhs, ConflictOldLength = regionRhs - regionLhs, ConflictRightOffset = bLhs, ConflictRightLength = bRhs - bLhs }); } commonOffset = regionRhs; } CopyCommon2(o.Length, ref commonOffset, result); return(result); }