/// <summary> /// Formats the results of a merge of <see cref="RawText"/> objects in a Git /// conformant way. This method also assumes that the <see cref="RawText"/> objects /// being merged are line oriented files which use LF as delimiter. This /// method will also use LF to separate chunks and conflict metadata, /// therefore it fits only to texts that are LF-separated lines. /// </summary> /// <param name="out">the outputstream where to write the textual presentation</param> /// <param name="res">the merge result which should be presented</param> /// <param name="seqName"> /// When a conflict is reported each conflicting range will get a /// name. This name is following the "<<<<<<< " or ">>>>>>> " /// conflict markers. The names for the sequences are given in /// this list /// </param> /// <param name="charsetName"> /// the name of the characterSet used when writing conflict /// metadata /// </param> public void formatMerge(BinaryWriter @out, MergeResult res, List <String> seqName, string charsetName) { String lastConflictingName = null; // is set to non-null whenever we are // in a conflict bool threeWayMerge = (res.getSequences().Count == 3); foreach (MergeChunk chunk in res) { RawText seq = (RawText)res.getSequences()[ chunk.getSequenceIndex()]; if (lastConflictingName != null && chunk.getConflictState() != MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE) { // found the end of an conflict @out.Write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); lastConflictingName = null; } if (chunk.getConflictState() == MergeChunk.ConflictState.FIRST_CONFLICTING_RANGE) { // found the start of an conflict @out.Write(("<<<<<<< " + seqName[chunk.getSequenceIndex()] + "\n").getBytes(charsetName)); lastConflictingName = seqName[chunk.getSequenceIndex()]; } else if (chunk.getConflictState() == MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE) { // found another conflicting chunk /* * In case of a non-three-way merge I'll add the name of the * conflicting chunk behind the equal signs. I also append the * name of the last conflicting chunk after the ending * greater-than signs. If somebody knows a better notation to * present non-three-way merges - feel free to correct here. */ lastConflictingName = seqName[chunk.getSequenceIndex()]; @out.Write((threeWayMerge ? "=======\n" : "======= " + lastConflictingName + "\n").getBytes(charsetName)); } // the lines with conflict-metadata are written. Now write the chunk for (int i = chunk.getBegin(); i < chunk.getEnd(); i++) { if (i > 0) { @out.Write('\n'); } seq.writeLine(@out.BaseStream, i); } } // one possible leftover: if the merge result ended with a conflict we // have to close the last conflict here if (lastConflictingName != null) { @out.Write('\n'); @out.Write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); } }
/// <summary> /// Formats the results of a merge of exactly two <see cref="RawText"/> objects in /// a Git conformant way. This convenience method accepts the names for the /// three sequences (base and the two merged sequences) as explicit /// parameters and doesn't require the caller to specify a List /// </summary> /// <param name="out"> /// the <see cref="BinaryWriter"/> where to write the textual /// presentation /// </param> /// <param name="res">the merge result which should be presented</param> /// <param name="baseName">the name ranges from the base should get</param> /// <param name="oursName">the name ranges from ours should get</param> /// <param name="theirsName">the name ranges from theirs should get</param> /// <param name="charsetName"> /// the name of the characterSet used when writing conflict /// metadata /// </param> public void formatMerge(BinaryWriter @out, MergeResult res, String baseName, String oursName, String theirsName, string charsetName) { var names = new List <String>(3); names.Add(baseName); names.Add(oursName); names.Add(theirsName); formatMerge(@out, res, names, charsetName); }
public MergeChunkIterator(MergeResult mergeResult) { _mergeResult = mergeResult; }
/// <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); }