示例#1
0
        /// <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 "&lt;&lt;&lt;&lt;&lt;&lt;&lt; " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; "
        /// 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));
            }
        }
示例#2
0
        /// <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);
        }
示例#3
0
 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);
        }