internal MergeResult <Sequence> Upcast() { var r = new MergeResult <Sequence> (sequences.UpcastTo <S, Sequence> ()); r.chunks = chunks; r.containsConflicts = containsConflicts; return(r); }
/// <summary> /// Formats the results of a merge of exactly two /// <see cref="NGit.Diff.RawText">NGit.Diff.RawText</see> /// 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="Sharpen.OutputStream">Sharpen.OutputStream</see> /// 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> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> public virtual void FormatMerge(OutputStream @out, MergeResult <RawText> res, string baseName , string oursName, string theirsName, string charsetName) { IList <string> names = new AList <string>(3); names.AddItem(baseName); names.AddItem(oursName); names.AddItem(theirsName); FormatMerge(@out, res, names, charsetName); }
/// <summary>Writes merged file content to the working tree.</summary> /// <remarks> /// Writes merged file content to the working tree. In case /// <see cref="inCore">inCore</see> /// is set and we don't have a working tree the content is written to a /// temporary file /// </remarks> /// <param name="result">the result of the content merge</param> /// <returns>the file to which the merged content was written</returns> /// <exception cref="System.IO.FileNotFoundException">System.IO.FileNotFoundException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private FilePath WriteMergedFile(MergeResult <RawText> result) { MergeFormatter fmt = new MergeFormatter(); FilePath of = null; FileOutputStream fos; if (!inCore) { FilePath workTree = db.WorkTree; if (workTree == null) { // TODO: This should be handled by WorkingTreeIterators which // support write operations throw new NGit.Errors.NotSupportedException(); } of = new FilePath(workTree, tw.PathString); FilePath parentFolder = of.GetParentFile(); if (!parentFolder.Exists()) { parentFolder.Mkdirs(); } fos = new FileOutputStream(of); try { fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING ); } finally { fos.Close(); } } else { if (!result.ContainsConflicts()) { // When working inCore, only trivial merges can be handled, // so we generate objects only in conflict free cases of = FilePath.CreateTempFile("merge_", "_temp", null); fos = new FileOutputStream(of); try { fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING ); } finally { fos.Close(); } } } return(of); }
/// <summary> /// Formats the results of a merge of /// <see cref="NGit.Diff.RawText">NGit.Diff.RawText</see> /// objects in a Git /// conformant way. This method also assumes that the /// <see cref="NGit.Diff.RawText">NGit.Diff.RawText</see> /// 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> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> public virtual void FormatMerge(OutputStream @out, MergeResult <RawText> res, IList <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 = res.GetSequences()[chunk.GetSequenceIndex()]; if (lastConflictingName != null && chunk.GetConflictState() != MergeChunk.ConflictState .NEXT_CONFLICTING_RANGE) { // found the end of an conflict @out.Write(Sharpen.Runtime.GetBytesForString((">>>>>>> " + lastConflictingName + "\n"), charsetName)); lastConflictingName = null; } if (chunk.GetConflictState() == MergeChunk.ConflictState.FIRST_CONFLICTING_RANGE) { // found the start of an conflict @out.Write(Sharpen.Runtime.GetBytesForString(("<<<<<<< " + seqName[chunk.GetSequenceIndex ()] + "\n"), charsetName)); lastConflictingName = seqName[chunk.GetSequenceIndex()]; } else { if (chunk.GetConflictState() == MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE) { // found another conflicting chunk lastConflictingName = seqName[chunk.GetSequenceIndex()]; @out.Write(Sharpen.Runtime.GetBytesForString((threeWayMerge ? "=======\n" : "======= " + lastConflictingName + "\n"), charsetName)); } } // the lines with conflict-metadata are written. Now write the chunk for (int i = chunk.GetBegin(); i < chunk.GetEnd(); i++) { seq.WriteLine(@out, i); @out.Write('\n'); } } // one possible leftover: if the merge result ended with a conflict we // have to close the last conflict here if (lastConflictingName != null) { @out.Write(Sharpen.Runtime.GetBytesForString((">>>>>>> " + lastConflictingName + "\n"), charsetName)); } }
/// <summary>Updates the index after a content merge has happened.</summary> /// <remarks> /// Updates the index after a content merge has happened. If no conflict has /// occurred this includes persisting the merged content to the object /// database. In case of conflicts this method takes care to write the /// correct stages to the index. /// </remarks> /// <param name="base"></param> /// <param name="ours"></param> /// <param name="theirs"></param> /// <param name="result"></param> /// <param name="of"></param> /// <exception cref="System.IO.FileNotFoundException">System.IO.FileNotFoundException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private void UpdateIndex(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, MergeResult <RawText> result, FilePath of) { if (result.ContainsConflicts()) { // a conflict occurred, the file will contain conflict markers // the index will be populated with the three stages and only the // workdir (if used) contains the halfways merged content Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); mergeResults.Put(tw.PathString, result.Upcast()); } else { // no conflict occurred, the file will contain fully merged content. // the index will be populated with the new merged version DirCacheEntry dce = new DirCacheEntry(tw.PathString); int newMode = MergeFileModes(tw.GetRawMode(0), tw.GetRawMode(1), tw.GetRawMode(2) ); // set the mode for the new content. Fall back to REGULAR_FILE if // you can't merge modes of OURS and THEIRS dce.FileMode = (newMode == FileMode.MISSING.GetBits()) ? FileMode.REGULAR_FILE : FileMode.FromBits(newMode); dce.LastModified = of.LastModified(); dce.SetLength((int)of.Length()); InputStream @is = new FileInputStream(of); try { dce.SetObjectId(GetObjectInserter().Insert(Constants.OBJ_BLOB, of.Length(), @is)); } finally { @is.Close(); if (inCore) { FileUtils.Delete(of); } } builder.Add(dce); } }
// An special edit which acts as a sentinel value by marking the end the // list of edits /// <summary>Does the three way merge between a common base and two sequences.</summary> /// <remarks>Does the three way merge between a common base and two sequences.</remarks> /// <?></?> /// <param name="cmp">comparison method for this execution.</param> /// <param name="base">the common base sequence</param> /// <param name="ours">the first sequence to be merged</param> /// <param name="theirs">the second sequence to be merged</param> /// <returns>the resulting content</returns> public MergeResult <S> Merge <S>(SequenceComparator <S> cmp, S @base, S ours, S theirs ) where S : Sequence { IList <S> sequences = new AList <S>(3); sequences.AddItem(@base); sequences.AddItem(ours); sequences.AddItem(theirs); MergeResult <S> result = new MergeResult <S>(sequences); if (ours.Size() == 0) { if (theirs.Size() != 0) { EditList theirsEdits = diffAlg.Diff(cmp, @base, theirs); if (!theirsEdits.IsEmpty()) { // we deleted, they modified -> Let their complete content // conflict with empty text result.Add(1, 0, 0, MergeChunk.ConflictState.FIRST_CONFLICTING_RANGE); result.Add(2, 0, theirs.Size(), MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE); } else { // we deleted, they didn't modify -> Let our deletion win result.Add(1, 0, 0, MergeChunk.ConflictState.NO_CONFLICT); } } else { // we and they deleted -> return a single chunk of nothing result.Add(1, 0, 0, MergeChunk.ConflictState.NO_CONFLICT); } return(result); } else { if (theirs.Size() == 0) { EditList oursEdits = diffAlg.Diff(cmp, @base, ours); if (!oursEdits.IsEmpty()) { // we modified, they deleted -> Let our complete content // conflict with empty text result.Add(1, 0, ours.Size(), MergeChunk.ConflictState.FIRST_CONFLICTING_RANGE); result.Add(2, 0, 0, MergeChunk.ConflictState.NEXT_CONFLICTING_RANGE); } else { // they deleted, we didn't modify -> Let their deletion win result.Add(2, 0, 0, MergeChunk.ConflictState.NO_CONFLICT); } return(result); } } EditList oursEdits_1 = diffAlg.Diff(cmp, @base, ours); Iterator <Edit> baseToOurs = oursEdits_1.Iterator(); EditList theirsEdits_1 = diffAlg.Diff(cmp, @base, theirs); Iterator <Edit> baseToTheirs = theirsEdits_1.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.GetEndA() < theirsEdit.GetBeginA()) { // 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.GetBeginA()) { result.Add(0, current, oursEdit.GetBeginA(), MergeChunk.ConflictState.NO_CONFLICT ); } result.Add(1, oursEdit.GetBeginB(), oursEdit.GetEndB(), MergeChunk.ConflictState. NO_CONFLICT); current = oursEdit.GetEndA(); oursEdit = NextEdit(baseToOurs); } else { if (theirsEdit.GetEndA() < oursEdit.GetBeginA()) { // 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.GetBeginA()) { result.Add(0, current, theirsEdit.GetBeginA(), MergeChunk.ConflictState.NO_CONFLICT ); } result.Add(2, theirsEdit.GetBeginB(), theirsEdit.GetEndB(), MergeChunk.ConflictState .NO_CONFLICT); current = theirsEdit.GetEndA(); 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.GetBeginA() != current && theirsEdit.GetBeginA() != current) { result.Add(0, current, Math.Min(oursEdit.GetBeginA(), theirsEdit.GetBeginA()), MergeChunk.ConflictState .NO_CONFLICT); } // set some initial values for the ranges in A and B which we // want to handle int oursBeginB = oursEdit.GetBeginB(); int theirsBeginB = theirsEdit.GetBeginB(); // harmonize the start of the ranges in A and B if (oursEdit.GetBeginA() < theirsEdit.GetBeginA()) { theirsBeginB -= theirsEdit.GetBeginA() - oursEdit.GetBeginA(); } else { oursBeginB -= oursEdit.GetBeginA() - theirsEdit.GetBeginA(); } // 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.GetEndA() >= nextTheirsEdit.GetBeginA()) { theirsEdit = nextTheirsEdit; nextTheirsEdit = NextEdit(baseToTheirs); } else { if (theirsEdit.GetEndA() >= nextOursEdit.GetBeginA()) { oursEdit = nextOursEdit; nextOursEdit = NextEdit(baseToOurs); } else { break; } } } // harmonize the end of the ranges in A and B int oursEndB = oursEdit.GetEndB(); int theirsEndB = theirsEdit.GetEndB(); if (oursEdit.GetEndA() < theirsEdit.GetEndA()) { oursEndB += theirsEdit.GetEndA() - oursEdit.GetEndA(); } else { theirsEndB += oursEdit.GetEndA() - theirsEdit.GetEndA(); } // A conflicting region is found. Strip off common lines in // in the beginning and the end of the conflicting region // Determine the minimum length of the conflicting areas in OURS // and THEIRS. Also determine how much bigger the conflicting // area in THEIRS is compared to OURS. All that is needed to // limit the search for common areas at the beginning or end // (the common areas cannot be bigger then the smaller // conflicting area. The delta is needed to know whether the // complete conflicting area is common in OURS and THEIRS. int minBSize = oursEndB - oursBeginB; int BSizeDelta = minBSize - (theirsEndB - theirsBeginB); if (BSizeDelta > 0) { minBSize -= BSizeDelta; } int commonPrefix = 0; while (commonPrefix < minBSize && cmp.Equals(ours, oursBeginB + commonPrefix, theirs , theirsBeginB + commonPrefix)) { commonPrefix++; } minBSize -= commonPrefix; int commonSuffix = 0; while (commonSuffix < minBSize && cmp.Equals(ours, oursEndB - commonSuffix - 1, theirs , theirsEndB - commonSuffix - 1)) { commonSuffix++; } minBSize -= commonSuffix; // Add the common lines at start of conflict if (commonPrefix > 0) { result.Add(1, oursBeginB, oursBeginB + commonPrefix, MergeChunk.ConflictState.NO_CONFLICT ); } // Add the conflict (Only if there is a conflict left to report) if (minBSize > 0 || BSizeDelta != 0) { result.Add(1, oursBeginB + commonPrefix, oursEndB - commonSuffix, MergeChunk.ConflictState .FIRST_CONFLICTING_RANGE); result.Add(2, theirsBeginB + commonPrefix, theirsEndB - commonSuffix, MergeChunk.ConflictState .NEXT_CONFLICTING_RANGE); } // Add the common lines at end of conflict if (commonSuffix > 0) { result.Add(1, oursEndB - commonSuffix, oursEndB, MergeChunk.ConflictState.NO_CONFLICT ); } current = Math.Max(oursEdit.GetEndA(), theirsEdit.GetEndA()); 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); }
/// <exception cref="System.IO.FileNotFoundException"></exception> /// <exception cref="System.InvalidOperationException"></exception> /// <exception cref="System.IO.IOException"></exception> private bool ContentMerge(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs) { MergeFormatter fmt = new MergeFormatter(); RawText baseText = @base == null ? RawText.EMPTY_TEXT : GetRawText(@base.EntryObjectId , db); // do the merge MergeResult <RawText> result = mergeAlgorithm.Merge(RawTextComparator.DEFAULT, baseText , GetRawText(ours.EntryObjectId, db), GetRawText(theirs.EntryObjectId, db)); FilePath of = null; FileOutputStream fos; if (!inCore) { FilePath workTree = db.WorkTree; if (workTree == null) { // TODO: This should be handled by WorkingTreeIterators which // support write operations throw new NotSupportedException(); } of = new FilePath(workTree, tw.PathString); fos = new FileOutputStream(of); try { fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING ); } finally { fos.Close(); } } else { if (!result.ContainsConflicts()) { // When working inCore, only trivial merges can be handled, // so we generate objects only in conflict free cases of = FilePath.CreateTempFile("merge_", "_temp", null); fos = new FileOutputStream(of); try { fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING ); } finally { fos.Close(); } } } if (result.ContainsConflicts()) { // a conflict occurred, the file will contain conflict markers // the index will be populated with the three stages and only the // workdir (if used) contains the halfways merged content Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); mergeResults.Put(tw.PathString, result.Upcast()); return(false); } else { // no conflict occurred, the file will contain fully merged content. // the index will be populated with the new merged version DirCacheEntry dce = new DirCacheEntry(tw.PathString); dce.FileMode = tw.GetFileMode(0); dce.LastModified = of.LastModified(); dce.SetLength((int)of.Length()); InputStream @is = new FileInputStream(of); try { dce.SetObjectId(oi.Insert(Constants.OBJ_BLOB, of.Length(), @is)); } finally { @is.Close(); if (inCore) { FileUtils.Delete(of); } } builder.Add(dce); return(true); } }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return(true); } if (IsIndexDirty()) { return(false); } DirCacheEntry ourDce = null; if (index == null || index.GetDirCacheEntry() == null) { // create a fake DCE, but only if ours is valid. ours is kept only // in case it is valid, so a null ourDce is ok in all other cases. if (NonTree(modeO)) { ourDce = new DirCacheEntry(tw.RawPath); ourDce.SetObjectId(tw.GetObjectId(T_OURS)); ourDce.FileMode = tw.GetFileMode(T_OURS); } } else { ourDce = index.GetDirCacheEntry(); } if (NonTree(modeO) && NonTree(modeT) && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS have equal content. Check the file mode if (modeO == modeT) { // content and mode of OURS and THEIRS are equal: it doesn't // matter which one we choose. OURS is chosen. Since the index // is clean (the index matches already OURS) we can keep the existing one Keep(ourDce); // no checkout needed! return(true); } else { // same content but different mode on OURS and THEIRS. // Try to merge the mode and report an error if this is // not possible. int newMode = MergeFileModes(modeB, modeO, modeT); if (newMode != FileMode.MISSING.GetBits()) { if (newMode == modeO) { // ours version is preferred Keep(ourDce); } else { // the preferred version THEIRS has a different mode // than ours. Check it out! if (IsWorktreeDirty(work)) { return(false); } // we know about length and lastMod only after we have written the new content. // This will happen later. Set these values to 0 for know. DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0); toBeCheckedOut.Put(tw.PathString, e); } return(true); } else { // FileModes are not mergeable. We found a conflict on modes. // For conflicting entries we don't know lastModified and length. Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.AddItem(tw.PathString); mergeResults.Put(tw.PathString, new MergeResult <RawText>(Sharpen.Collections.EmptyList <RawText>()).Upcast()); } return(true); } } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. We can keep the existing entry. Keep(ourDce); // no checkout needed! return(true); } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return(false); } if (NonTree(modeT)) { // we know about length and lastMod only after we have written // the new content. // This will happen later. Set these values to 0 for know. DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return(true); } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeDeleted.AddItem(tw.PathString); return(true); } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return(true); } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty(work)) { return(false); } // Don't attempt to resolve submodule link conflicts if (IsGitLink(modeO) || IsGitLink(modeT)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.AddItem(tw.PathString); return(true); } MergeResult <RawText> result = ContentMerge(@base, ours, theirs); FilePath of = WriteMergedFile(result); UpdateIndex(@base, ours, theirs, result, of); if (result.ContainsConflicts()) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } else { if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE , T_THEIRS)))) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); // OURS was deleted checkout THEIRS if (modeO == 0) { // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return(false); } if (NonTree(modeT)) { if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } } } unmergedPaths.AddItem(tw.PathString); // generate a MergeResult for the deleted file mergeResults.Put(tw.PathString, ContentMerge(@base, ours, theirs).Upcast()); } } } return(true); }
public _Iterator_137(MergeResult <S> _enclosing) { this._enclosing = _enclosing; }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return(true); } if (IsIndexDirty()) { return(false); } if (NonTree(modeO) && modeO == modeT && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS are equal: it doesn't matter which one we choose. // OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return(true); } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return(true); } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty()) { return(false); } if (NonTree(modeT)) { DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return(true); } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeCheckedOut.Put(tw.PathString, null); return(true); } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return(true); } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty()) { return(false); } if (!ContentMerge(@base, ours, theirs)) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } else { if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE , T_THEIRS)))) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); // OURS was deleted checkout THEIRS if (modeO == 0) { // Check worktree before checking out THEIRS if (IsWorktreeDirty()) { return(false); } if (NonTree(modeT)) { if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } } } unmergedPaths.AddItem(tw.PathString); // generate a MergeResult for the deleted file RawText baseText = @base == null ? RawText.EMPTY_TEXT : GetRawText(@base.EntryObjectId , db); RawText ourText = ours == null ? RawText.EMPTY_TEXT : GetRawText(ours.EntryObjectId , db); RawText theirsText = theirs == null ? RawText.EMPTY_TEXT : GetRawText(theirs.EntryObjectId , db); MergeResult <RawText> result = mergeAlgorithm.Merge(RawTextComparator.DEFAULT, baseText , ourText, theirsText); mergeResults.Put(tw.PathString, result.Upcast()); } } } return(true); }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return(true); } if (IsIndexDirty()) { return(false); } if (NonTree(modeO) && NonTree(modeT) && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS have equal content. Check the file mode if (modeO == modeT) { // content and mode of OURS and THEIRS are equal: it doesn't // matter which one we choose. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return(true); } else { // same content but different mode on OURS and THEIRS. // Try to merge the mode and report an error if this is // not possible. int newMode = MergeFileModes(modeB, modeO, modeT); if (newMode != FileMode.MISSING.GetBits()) { if (newMode == modeO) { // ours version is preferred Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); } else { // the preferred version THEIRS has a different mode // than ours. Check it out! if (IsWorktreeDirty(work)) { return(false); } DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); toBeCheckedOut.Put(tw.PathString, e); } return(true); } else { // FileModes are not mergeable. We found a conflict on modes Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); mergeResults.Put(tw.PathString, new MergeResult <RawText>(Sharpen.Collections.EmptyList <RawText>()).Upcast()); } return(true); } } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return(true); } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return(false); } if (NonTree(modeT)) { DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return(true); } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeDeleted.AddItem(tw.PathString); return(true); } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return(true); } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return(true); } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty(work)) { return(false); } MergeResult <RawText> result = ContentMerge(@base, ours, theirs); FilePath of = WriteMergedFile(result); UpdateIndex(@base, ours, theirs, result, of); if (result.ContainsConflicts()) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } else { if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE , T_THEIRS)))) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); // OURS was deleted checkout THEIRS if (modeO == 0) { // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return(false); } if (NonTree(modeT)) { if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } } } unmergedPaths.AddItem(tw.PathString); // generate a MergeResult for the deleted file mergeResults.Put(tw.PathString, ContentMerge(@base, ours, theirs).Upcast()); } } } return(true); }