/// <exception cref="System.IO.IOException"></exception> protected internal override bool MergeImpl() { tw.AddTree(MergeBase()); tw.AddTree(sourceTrees[0]); tw.AddTree(sourceTrees[1]); bool hasConflict = false; builder = cache.Builder(); while (tw.Next()) { int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); if (modeO == modeT && tw.IdEqual(T_OURS, T_THEIRS)) { Add(T_OURS, DirCacheEntry.STAGE_0); continue; } int modeB = tw.GetRawMode(T_BASE); if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { Add(T_THEIRS, DirCacheEntry.STAGE_0); } else { if (modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { Add(T_OURS, DirCacheEntry.STAGE_0); } else { if (NonTree(modeB)) { Add(T_BASE, DirCacheEntry.STAGE_1); hasConflict = true; } if (NonTree(modeO)) { Add(T_OURS, DirCacheEntry.STAGE_2); hasConflict = true; } if (NonTree(modeT)) { Add(T_THEIRS, DirCacheEntry.STAGE_3); hasConflict = true; } if (tw.IsSubtree) { tw.EnterSubtree(); } } } } builder.Finish(); builder = null; if (hasConflict) { return(false); } try { ObjectInserter odi = GetObjectInserter(); resultTree = cache.WriteTree(odi); odi.Flush(); return(true); } catch (UnmergedPathException) { resultTree = null; return(false); } }
/// <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); } 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 occured /// </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 modeI = tw.GetRawMode(T_INDEX); // Each index entry has to match ours, means: it has to be clean if (NonTree(modeI) && !(tw.IdEqual(T_INDEX, T_OURS) && modeO == modeI)) { failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_INDEX); return(false); } int modeT = tw.GetRawMode(T_THEIRS); if (NonTree(modeO) && modeO == modeT && tw.IdEqual(T_OURS, T_THEIRS)) { // ours and theirs are equal: it doesn'nt matter // which one we choose. OURS is choosen here. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return(true); } int modeB = tw.GetRawMode(T_BASE); if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to base. All changes must be in // OURS. Choose OURS. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); return(true); } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to base. All changes must be in // THEIRS. Choose THEIRS. 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)) { if (!inCore) { // We are going to update the worktree. Make sure the worktree // is not modified if (work != null && (!NonTree(work.EntryRawMode) || work.IsModified(index.GetDirCacheEntry (), true))) { failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_WORKTREE); return(false); } } if (!ContentMerge(@base, ours, theirs)) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } 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); }
/// <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); }