/// <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 &lt;a href=
		/// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
		/// &gt;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;
		}
Example #2
0
 internal DirCacheBuildIterator(NGit.Dircache.DirCacheBuildIterator p, DirCacheTree
                                dct) : base(p, dct)
 {
     builder = p.builder;
 }
		/// <exception cref="System.IO.IOException"></exception>
		protected internal override bool MergeImpl()
		{
			bool implicitDirCache = false;
			if (dircache == null)
			{
				dircache = GetRepository().LockDirCache();
				implicitDirCache = true;
			}
			try
			{
				builder = dircache.Builder();
				DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
				tw = new NameConflictTreeWalk(db);
				tw.AddTree(MergeBase());
				tw.AddTree(sourceTrees[0]);
				tw.AddTree(sourceTrees[1]);
				tw.AddTree(buildIt);
				if (workingTreeIterator != null)
				{
					tw.AddTree(workingTreeIterator);
				}
				while (tw.Next())
				{
					if (!ProcessEntry(tw.GetTree<CanonicalTreeParser>(T_BASE), tw.GetTree<CanonicalTreeParser
						>(T_OURS), tw.GetTree<CanonicalTreeParser>(T_THEIRS), tw.GetTree<DirCacheBuildIterator
						>(T_INDEX), (workingTreeIterator == null) ? null : tw.GetTree<WorkingTreeIterator
						>(T_FILE)))
					{
						CleanUp();
						return false;
					}
					if (tw.IsSubtree && enterSubtree)
					{
						tw.EnterSubtree();
					}
				}
				if (!inCore)
				{
					// All content-merges are successfully done. If we can now write the
					// new index we are on quite safe ground. Even if the checkout of
					// files coming from "theirs" fails the user can work around such
					// failures by checking out the index again.
					if (!builder.Commit())
					{
						CleanUp();
						throw new IndexWriteException();
					}
					builder = null;
					// No problem found. The only thing left to be done is to checkout
					// all files from "theirs" which have been selected to go into the
					// new index.
					Checkout();
				}
				else
				{
					builder.Finish();
					builder = null;
				}
				if (GetUnmergedPaths().IsEmpty())
				{
					resultTree = dircache.WriteTree(oi);
					return true;
				}
				else
				{
					resultTree = null;
					return false;
				}
			}
			finally
			{
				if (implicitDirCache)
				{
					dircache.Unlock();
				}
			}
		}
Example #4
0
        /// <summary>Here the main work is done.</summary>
        /// <remarks>
        /// Here the main work is done. This method is called for each existing path
        /// in head, index and merge. This method decides what to do with the
        /// corresponding index entry: keep it, update it, remove it or mark a
        /// conflict.
        /// </remarks>
        /// <param name="h">the entry for the head</param>
        /// <param name="m">the entry for the merge</param>
        /// <param name="i">the entry for the index</param>
        /// <param name="f">the file in the working tree</param>
        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
        internal virtual void ProcessEntry(CanonicalTreeParser h, CanonicalTreeParser m, 
			DirCacheBuildIterator i, WorkingTreeIterator f)
        {
            DirCacheEntry dce = i != null ? i.GetDirCacheEntry() : null;
            string name = walk.PathString;
            if (m != null && !IsValidPath(m))
            {
                throw new InvalidPathException(m.EntryPathString);
            }
            if (i == null && m == null && h == null)
            {
                // File/Directory conflict case #20
                if (walk.IsDirectoryFileConflict())
                {
                    // TODO: check whether it is always correct to report a conflict here
                    Conflict(name, null, null, null);
                }
                // file only exists in working tree -> ignore it
                return;
            }
            ObjectId iId = (i == null ? null : i.EntryObjectId);
            ObjectId mId = (m == null ? null : m.EntryObjectId);
            ObjectId hId = (h == null ? null : h.EntryObjectId);
            FileMode iMode = (i == null ? null : i.EntryFileMode);
            FileMode mMode = (m == null ? null : m.EntryFileMode);
            FileMode hMode = (h == null ? null : h.EntryFileMode);
            // The information whether head,index,merge iterators are currently
            // pointing to file/folder/non-existing is encoded into this variable.
            //
            // To decode write down ffMask in hexadecimal form. The last digit
            // represents the state for the merge iterator, the second last the
            // state for the index iterator and the third last represents the state
            // for the head iterator. The hexadecimal constant "F" stands for
            // "file",
            // an "D" stands for "directory" (tree), and a "0" stands for
            // non-existing
            //
            // Examples:
            // ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
            // ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
            int ffMask = 0;
            if (h != null)
            {
                ffMask = FileMode.TREE.Equals(hMode) ? unchecked((int)(0xD00)) : unchecked((int)(
                    0xF00));
            }
            if (i != null)
            {
                ffMask |= FileMode.TREE.Equals(iMode) ? unchecked((int)(0x0D0)) : unchecked((int)
                    (0x0F0));
            }
            if (m != null)
            {
                ffMask |= FileMode.TREE.Equals(mMode) ? unchecked((int)(0x00D)) : unchecked((int)
                    (0x00F));
            }
            // Check whether we have a possible file/folder conflict. Therefore we
            // need a least one file and one folder.
            if (((ffMask & unchecked((int)(0x222))) != unchecked((int)(0x000))) && (((ffMask
                & unchecked((int)(0x00F))) == unchecked((int)(0x00D))) || ((ffMask & unchecked((
                int)(0x0F0))) == unchecked((int)(0x0D0))) || ((ffMask & unchecked((int)(0xF00)))
                 == unchecked((int)(0xD00)))))
            {
                switch (ffMask)
                {
                    case unchecked((int)(0xDDF)):
                    {
                        // There are 3*3*3=27 possible combinations of file/folder
                        // conflicts. Some of them are not-relevant because
                        // they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
                        // switch processes all relevant cases.
                        // 1 2
                        if (IsModified(name))
                        {
                            Conflict(name, dce, h, m);
                        }
                        else
                        {
                            // 1
                            Update(name, mId, mMode);
                        }
                        // 2
                        break;
                    }

                    case unchecked((int)(0xDFD)):
                    {
                        // 3 4
                        // CAUTION: I put it into removed instead of updated, because
                        // that's what our tests expect
                        // updated.put(name, mId);
                        Remove(name);
                        break;
                    }

                    case unchecked((int)(0xF0D)):
                    {
                        // 18
                        Remove(name);
                        break;
                    }

                    case unchecked((int)(0xDFF)):
                    case unchecked((int)(0xFDD)):
                    {
                        // 5 6
                        // 10 11
                        // TODO: make use of tree extension as soon as available in jgit
                        // we would like to do something like
                        // if (!equalIdAndMode(iId, iMode, mId, mMode)
                        //   conflict(name, i.getDirCacheEntry(), h, m);
                        // But since we don't know the id of a tree in the index we do
                        // nothing here and wait that conflicts between index and merge
                        // are found later
                        break;
                    }

                    case unchecked((int)(0xD0F)):
                    {
                        // 19
                        Update(name, mId, mMode);
                        break;
                    }

                    case unchecked((int)(0xDF0)):
                    case unchecked((int)(0x0FD)):
                    {
                        // conflict without a rule
                        // 15
                        Conflict(name, dce, h, m);
                        break;
                    }

                    case unchecked((int)(0xFDF)):
                    {
                        // 7 8 9
                        if (EqualIdAndMode(hId, hMode, mId, mMode))
                        {
                            if (IsModified(name))
                            {
                                Conflict(name, dce, h, m);
                            }
                            else
                            {
                                // 8
                                Update(name, mId, mMode);
                            }
                        }
                        else
                        {
                            // 7
                            if (!IsModified(name))
                            {
                                Update(name, mId, mMode);
                            }
                            else
                            {
                                // 9
                                // To be confirmed - this case is not in the table.
                                Conflict(name, dce, h, m);
                            }
                        }
                        break;
                    }

                    case unchecked((int)(0xFD0)):
                    {
                        // keep without a rule
                        Keep(dce);
                        break;
                    }

                    case unchecked((int)(0xFFD)):
                    {
                        // 12 13 14
                        if (EqualIdAndMode(hId, hMode, iId, iMode))
                        {
                            if (f == null || f.IsModified(dce, true))
                            {
                                Conflict(name, dce, h, m);
                            }
                            else
                            {
                                Remove(name);
                            }
                        }
                        else
                        {
                            Conflict(name, dce, h, m);
                        }
                        break;
                    }

                    case unchecked((int)(0x0DF)):
                    {
                        // 16 17
                        if (!IsModified(name))
                        {
                            Update(name, mId, mMode);
                        }
                        else
                        {
                            Conflict(name, dce, h, m);
                        }
                        break;
                    }

                    default:
                    {
                        Keep(dce);
                        break;
                    }
                }
                return;
            }
            // if we have no file at all then there is nothing to do
            if ((ffMask & unchecked((int)(0x222))) == 0)
            {
                return;
            }
            if ((ffMask == unchecked((int)(0x00F))) && f != null && FileMode.TREE.Equals(f.EntryFileMode
                ))
            {
                // File/Directory conflict case #20
                Conflict(name, null, h, m);
            }
            if (i == null)
            {
                // make sure not to overwrite untracked files
                if (f != null)
                {
                    // A submodule is not a file. We should ignore it
                    if (!FileMode.GITLINK.Equals(mMode))
                    {
                        // a dirty worktree: the index is empty but we have a
                        // workingtree-file
                        if (mId == null || !EqualIdAndMode(mId, mMode, f.EntryObjectId, f.EntryFileMode))
                        {
                            Conflict(name, null, h, m);
                            return;
                        }
                    }
                }
                if (h == null)
                {
                    Update(name, mId, mMode);
                }
                else
                {
                    // 1
                    if (m == null)
                    {
                        Remove(name);
                    }
                    else
                    {
                        // 2
                        Update(name, mId, mMode);
                    }
                }
            }
            else
            {
                // 3
                if (h == null)
                {
                    if (m == null || EqualIdAndMode(mId, mMode, iId, iMode))
                    {
                        if (m == null && walk.IsDirectoryFileConflict())
                        {
                            if (dce != null && (f == null || f.IsModified(dce, true)))
                            {
                                Conflict(name, dce, h, m);
                            }
                            else
                            {
                                Remove(name);
                            }
                        }
                        else
                        {
                            Keep(dce);
                        }
                    }
                    else
                    {
                        Conflict(name, dce, h, m);
                    }
                }
                else
                {
                    if (m == null)
                    {
                        if (iMode == FileMode.GITLINK)
                        {
                            // Submodules that disappear from the checkout must
                            // be removed from the index, but not deleted from disk.
                            Remove(name);
                        }
                        else
                        {
                            if (EqualIdAndMode(hId, hMode, iId, iMode))
                            {
                                if (f == null || f.IsModified(dce, true))
                                {
                                    Conflict(name, dce, h, m);
                                }
                                else
                                {
                                    Remove(name);
                                }
                            }
                            else
                            {
                                Conflict(name, dce, h, m);
                            }
                        }
                    }
                    else
                    {
                        if (!EqualIdAndMode(hId, hMode, mId, mMode) && !EqualIdAndMode(hId, hMode, iId, iMode
                            ) && !EqualIdAndMode(mId, mMode, iId, iMode))
                        {
                            Conflict(name, dce, h, m);
                        }
                        else
                        {
                            if (EqualIdAndMode(hId, hMode, iId, iMode) && !EqualIdAndMode(mId, mMode, iId, iMode
                                ))
                            {
                                // For submodules just update the index with the new SHA-1
                                if (dce != null && FileMode.GITLINK.Equals(dce.FileMode))
                                {
                                    Update(name, mId, mMode);
                                }
                                else
                                {
                                    if (dce != null && (f == null || f.IsModified(dce, true)))
                                    {
                                        Conflict(name, dce, h, m);
                                    }
                                    else
                                    {
                                        Update(name, mId, mMode);
                                    }
                                }
                            }
                            else
                            {
                                Keep(dce);
                            }
                        }
                    }
                }
            }
        }
Example #5
0
        /// <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 &lt;a href=
        /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
        /// &gt;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())
                            {
                                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())
                {
                    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;
                }
                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())
                            {
                                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>
		/// Processing an entry in the context of
		/// <see cref="PrescanOneTree()">PrescanOneTree()</see>
		/// when only
		/// one tree is given
		/// </summary>
		/// <param name="m">the tree to merge</param>
		/// <param name="i">the index</param>
		/// <param name="f">the working tree</param>
		internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i
			, WorkingTreeIterator f)
		{
			if (m != null)
			{
				if (i == null || f == null || !m.IdEqual(i) || (i.GetDirCacheEntry() != null && (
					f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0)))
				{
					Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
				}
				else
				{
					Keep(i.GetDirCacheEntry());
				}
			}
			else
			{
				if (f != null)
				{
					if (walk.IsDirectoryFileConflict())
					{
						conflicts.AddItem(walk.PathString);
					}
					else
					{
						if (i != null)
						{
							// ... and the working dir contained a file or folder ->
							// add it to the removed set and remove it from
							// conflicts set
							Remove(i.EntryPathString);
							conflicts.Remove(i.EntryPathString);
						}
					}
				}
				else
				{
					if (i.GetDirCacheEntry().Stage == 0)
					{
						Keep(i.GetDirCacheEntry());
					}
				}
			}
		}
Example #7
0
        /// <summary>
        /// Processing an entry in the context of
        /// <see cref="PrescanOneTree()">PrescanOneTree()</see>
        /// when only
        /// one tree is given
        /// </summary>
        /// <param name="m">the tree to merge</param>
        /// <param name="i">the index</param>
        /// <param name="f">the working tree</param>
        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
        internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i
			, WorkingTreeIterator f)
        {
            if (m != null)
            {
                if (!IsValidPath(m))
                {
                    throw new InvalidPathException(m.EntryPathString);
                }
                // There is an entry in the merge commit. Means: we want to update
                // what's currently in the index and working-tree to that one
                if (i == null)
                {
                    // The index entry is missing
                    if (f != null && !FileMode.TREE.Equals(f.EntryFileMode) && !f.IsEntryIgnored())
                    {
                        // don't overwrite an untracked and not ignored file
                        conflicts.AddItem(walk.PathString);
                    }
                    else
                    {
                        Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
                    }
                }
                else
                {
                    if (f == null || !m.IdEqual(i))
                    {
                        // The working tree file is missing or the merge content differs
                        // from index content
                        Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
                    }
                    else
                    {
                        if (i.GetDirCacheEntry() != null)
                        {
                            // The index contains a file (and not a folder)
                            if (f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0)
                            {
                                // The working tree file is dirty or the index contains a
                                // conflict
                                Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
                            }
                            else
                            {
                                // update the timestamp of the index with the one from the
                                // file if not set, as we are sure to be in sync here.
                                DirCacheEntry entry = i.GetDirCacheEntry();
                                if (entry.LastModified == 0)
                                {
                                    entry.LastModified = f.GetEntryLastModified();
                                }
                                Keep(entry);
                            }
                        }
                        else
                        {
                            // The index contains a folder
                            Keep(i.GetDirCacheEntry());
                        }
                    }
                }
            }
            else
            {
                // There is no entry in the merge commit. Means: we want to delete
                // what's currently in the index and working tree
                if (f != null)
                {
                    // There is a file/folder for that path in the working tree
                    if (walk.IsDirectoryFileConflict())
                    {
                        conflicts.AddItem(walk.PathString);
                    }
                    else
                    {
                        // No file/folder conflict exists. All entries are files or
                        // all entries are folders
                        if (i != null)
                        {
                            // ... and the working tree contained a file or folder
                            // -> add it to the removed set and remove it from
                            // conflicts set
                            Remove(i.EntryPathString);
                            conflicts.Remove(i.EntryPathString);
                        }
                    }
                }
            }
        }
		/// <summary>
		/// Processing an entry in the context of
		/// <see cref="PrescanOneTree()">PrescanOneTree()</see>
		/// when only
		/// one tree is given
		/// </summary>
		/// <param name="m">the tree to merge</param>
		/// <param name="i">the index</param>
		/// <param name="f">the working tree</param>
		/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
		internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i
			, WorkingTreeIterator f)
		{
			if (m != null)
			{
				// There is an entry in the merge commit. Means: we want to update
				// what's currently in the index and working-tree to that one
				if (i == null)
				{
					// The index entry is missing
					if (f != null && !FileMode.TREE.Equals(f.EntryFileMode) && !f.IsEntryIgnored())
					{
						// don't overwrite an untracked and not ignored file
						conflicts.AddItem(walk.PathString);
					}
					else
					{
						Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
					}
				}
				else
				{
					if (f == null || !m.IdEqual(i))
					{
						// The working tree file is missing or the merge content differs
						// from index content
						Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
					}
					else
					{
						if (i.GetDirCacheEntry() != null)
						{
							// The index contains a file (and not a folder)
							if (f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0)
							{
								// The working tree file is dirty or the index contains a
								// conflict
								Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
							}
							else
							{
								Keep(i.GetDirCacheEntry());
							}
						}
						else
						{
							// The index contains a folder
							Keep(i.GetDirCacheEntry());
						}
					}
				}
			}
			else
			{
				// There is no entry in the merge commit. Means: we want to delete
				// what's currently in the index and working tree
				if (f != null)
				{
					// There is a file/folder for that path in the working tree
					if (walk.IsDirectoryFileConflict())
					{
						conflicts.AddItem(walk.PathString);
					}
					else
					{
						// No file/folder conflict exists. All entries are files or
						// all entries are folders
						if (i != null)
						{
							// ... and the working tree contained a file or folder
							// -> add it to the removed set and remove it from
							// conflicts set
							Remove(i.EntryPathString);
							conflicts.Remove(i.EntryPathString);
						}
					}
				}
				else
				{
					// untracked file, neither contained in tree to merge
					// nor in index
					// There is no file/folder for that path in the working tree.
					// The only entry we have is the index entry. If that entry is a
					// conflict simply remove it. Otherwise keep that entry in the
					// index
					if (i.GetDirCacheEntry().Stage == 0)
					{
						Keep(i.GetDirCacheEntry());
					}
				}
			}
		}
		/// <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 &lt;a href=
		/// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
		/// &gt;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;
		}
Example #10
0
        /// <summary>Here the main work is done.</summary>
        /// <remarks>
        /// Here the main work is done. This method is called for each existing path
        /// in head, index and merge. This method decides what to do with the
        /// corresponding index entry: keep it, update it, remove it or mark a
        /// conflict.
        /// </remarks>
        /// <param name="h">the entry for the head</param>
        /// <param name="m">the entry for the merge</param>
        /// <param name="i">the entry for the index</param>
        /// <param name="f">the file in the working tree</param>
        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
        internal virtual void ProcessEntry(AbstractTreeIterator h, AbstractTreeIterator m
                                           , DirCacheBuildIterator i, WorkingTreeIterator f)
        {
            DirCacheEntry dce;
            string        name = walk.PathString;

            if (i == null && m == null && h == null)
            {
                // File/Directory conflict case #20
                if (walk.IsDirectoryFileConflict())
                {
                    // TODO: check whether it is always correct to report a conflict here
                    Conflict(name, null, null, null);
                }
                // file only exists in working tree -> ignore it
                return;
            }
            ObjectId iId = (i == null ? null : i.EntryObjectId);
            ObjectId mId = (m == null ? null : m.EntryObjectId);
            ObjectId hId = (h == null ? null : h.EntryObjectId);
            // The information whether head,index,merge iterators are currently
            // pointing to file/folder/non-existing is encoded into this variable.
            //
            // To decode write down ffMask in hexadecimal form. The last digit
            // represents the state for the merge iterator, the second last the
            // state for the index iterator and the third last represents the state
            // for the head iterator. The hexadecimal constant "F" stands for
            // "file",
            // an "D" stands for "directory" (tree), and a "0" stands for
            // non-existing
            //
            // Examples:
            // ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
            // ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
            int ffMask = 0;

            if (h != null)
            {
                ffMask = FileMode.TREE.Equals(h.EntryFileMode) ? unchecked ((int)(0xD00)) : unchecked (
                    (int)(0xF00));
            }
            if (i != null)
            {
                ffMask |= FileMode.TREE.Equals(i.EntryFileMode) ? unchecked ((int)(0x0D0)) : unchecked (
                    (int)(0x0F0));
            }
            if (m != null)
            {
                ffMask |= FileMode.TREE.Equals(m.EntryFileMode) ? unchecked ((int)(0x00D)) : unchecked (
                    (int)(0x00F));
            }
            // Check whether we have a possible file/folder conflict. Therefore we
            // need a least one file and one folder.
            if (((ffMask & unchecked ((int)(0x222))) != unchecked ((int)(0x000))) && (((ffMask
                                                                                        & unchecked ((int)(0x00F))) == unchecked ((int)(0x00D))) || ((ffMask & unchecked ((
                                                                                                                                                                              int)(0x0F0))) == unchecked ((int)(0x0D0))) || ((ffMask & unchecked ((int)(0xF00)))
                                                                                                                                                                                                                             == unchecked ((int)(0xD00)))))
            {
                switch (ffMask)
                {
                case unchecked ((int)(0xDDF)):
                {
                    // There are 3*3*3=27 possible combinations of file/folder
                    // conflicts. Some of them are not-relevant because
                    // they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
                    // switch processes all relevant cases.
                    // 1 2
                    if (IsModified(name))
                    {
                        Conflict(name, i.GetDirCacheEntry(), h, m);
                    }
                    else
                    {
                        // 1
                        Update(name, m.EntryObjectId, m.EntryFileMode);
                    }
                    // 2
                    break;
                }

                case unchecked ((int)(0xDFD)):
                {
                    // 3 4
                    // CAUTION: I put it into removed instead of updated, because
                    // that's what our tests expect
                    // updated.put(name, mId);
                    Remove(name);
                    break;
                }

                case unchecked ((int)(0xF0D)):
                {
                    // 18
                    Remove(name);
                    break;
                }

                case unchecked ((int)(0xDFF)):
                case unchecked ((int)(0xFDD)):
                {
                    // 5 6
                    // 10 11
                    // TODO: make use of tree extension as soon as available in jgit
                    // we would like to do something like
                    // if (!iId.equals(mId))
                    //   conflict(name, i.getDirCacheEntry(), h, m);
                    // But since we don't know the id of a tree in the index we do
                    // nothing here and wait that conflicts between index and merge
                    // are found later
                    break;
                }

                case unchecked ((int)(0xD0F)):
                {
                    // 19
                    Update(name, mId, m.EntryFileMode);
                    break;
                }

                case unchecked ((int)(0xDF0)):
                case unchecked ((int)(0x0FD)):
                {
                    // conflict without a rule
                    // 15
                    Conflict(name, (i != null) ? i.GetDirCacheEntry() : null, h, m);
                    break;
                }

                case unchecked ((int)(0xFDF)):
                {
                    // 7 8 9
                    if (hId.Equals(mId))
                    {
                        if (IsModified(name))
                        {
                            Conflict(name, i.GetDirCacheEntry(), h, m);
                        }
                        else
                        {
                            // 8
                            Update(name, mId, m.EntryFileMode);
                        }
                    }
                    else
                    {
                        // 7
                        if (!IsModified(name))
                        {
                            Update(name, mId, m.EntryFileMode);
                        }
                        else
                        {
                            // 9
                            // To be confirmed - this case is not in the table.
                            Conflict(name, i.GetDirCacheEntry(), h, m);
                        }
                    }
                    break;
                }

                case unchecked ((int)(0xFD0)):
                {
                    // keep without a rule
                    Keep(i.GetDirCacheEntry());
                    break;
                }

                case unchecked ((int)(0xFFD)):
                {
                    // 12 13 14
                    if (hId.Equals(iId))
                    {
                        dce = i.GetDirCacheEntry();
                        if (f == null || f.IsModified(dce, true))
                        {
                            Conflict(name, i.GetDirCacheEntry(), h, m);
                        }
                        else
                        {
                            Remove(name);
                        }
                    }
                    else
                    {
                        Conflict(name, i.GetDirCacheEntry(), h, m);
                    }
                    break;
                }

                case unchecked ((int)(0x0DF)):
                {
                    // 16 17
                    if (!IsModified(name))
                    {
                        Update(name, mId, m.EntryFileMode);
                    }
                    else
                    {
                        Conflict(name, i.GetDirCacheEntry(), h, m);
                    }
                    break;
                }

                default:
                {
                    Keep(i.GetDirCacheEntry());
                    break;
                }
                }
                return;
            }
            // if we have no file at all then there is nothing to do
            if ((ffMask & unchecked ((int)(0x222))) == 0)
            {
                return;
            }
            if ((ffMask == unchecked ((int)(0x00F))) && f != null && FileMode.TREE.Equals(f.EntryFileMode
                                                                                          ))
            {
                // File/Directory conflict case #20
                Conflict(name, null, h, m);
            }
            if (i == null)
            {
                // make sure not to overwrite untracked files
                if (f != null)
                {
                    // a dirty worktree: the index is empty but we have a
                    // workingtree-file
                    if (mId == null || !mId.Equals(f.EntryObjectId))
                    {
                        Conflict(name, null, h, m);
                        return;
                    }
                }
                if (h == null)
                {
                    Update(name, mId, m.EntryFileMode);
                }
                else
                {
                    // 1
                    if (m == null)
                    {
                        Remove(name);
                    }
                    else
                    {
                        // 2
                        Update(name, mId, m.EntryFileMode);
                    }
                }
            }
            else
            {
                // 3
                dce = i.GetDirCacheEntry();
                if (h == null)
                {
                    if (m == null || mId.Equals(iId))
                    {
                        if (m == null && walk.IsDirectoryFileConflict())
                        {
                            if (dce != null && (f == null || f.IsModified(dce, true)))
                            {
                                Conflict(name, i.GetDirCacheEntry(), h, m);
                            }
                            else
                            {
                                Remove(name);
                            }
                        }
                        else
                        {
                            Keep(i.GetDirCacheEntry());
                        }
                    }
                    else
                    {
                        Conflict(name, i.GetDirCacheEntry(), h, m);
                    }
                }
                else
                {
                    if (m == null)
                    {
                        if (hId.Equals(iId))
                        {
                            if (f == null || f.IsModified(dce, true))
                            {
                                Conflict(name, i.GetDirCacheEntry(), h, m);
                            }
                            else
                            {
                                Remove(name);
                            }
                        }
                        else
                        {
                            Conflict(name, i.GetDirCacheEntry(), h, m);
                        }
                    }
                    else
                    {
                        if (!hId.Equals(mId) && !hId.Equals(iId) && !mId.Equals(iId))
                        {
                            Conflict(name, i.GetDirCacheEntry(), h, m);
                        }
                        else
                        {
                            if (hId.Equals(iId) && !mId.Equals(iId))
                            {
                                if (dce != null && (f == null || f.IsModified(dce, true)))
                                {
                                    Conflict(name, i.GetDirCacheEntry(), h, m);
                                }
                                else
                                {
                                    Update(name, mId, m.EntryFileMode);
                                }
                            }
                            else
                            {
                                Keep(i.GetDirCacheEntry());
                            }
                        }
                    }
                }
            }
        }
Example #11
0
 /// <summary>
 /// Processing an entry in the context of
 /// <see cref="PrescanOneTree()">PrescanOneTree()</see>
 /// when only
 /// one tree is given
 /// </summary>
 /// <param name="m">the tree to merge</param>
 /// <param name="i">the index</param>
 /// <param name="f">the working tree</param>
 /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i
                                    , WorkingTreeIterator f)
 {
     if (m != null)
     {
         // There is an entry in the merge commit. Means: we want to update
         // what's currently in the index and working-tree to that one
         if (i == null)
         {
             // The index entry is missing
             if (f != null && !FileMode.TREE.Equals(f.EntryFileMode) && !f.IsEntryIgnored())
             {
                 // don't overwrite an untracked and not ignored file
                 conflicts.AddItem(walk.PathString);
             }
             else
             {
                 Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
             }
         }
         else
         {
             if (f == null || !m.IdEqual(i))
             {
                 // The working tree file is missing or the merge content differs
                 // from index content
                 Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
             }
             else
             {
                 if (i.GetDirCacheEntry() != null)
                 {
                     // The index contains a file (and not a folder)
                     if (f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0)
                     {
                         // The working tree file is dirty or the index contains a
                         // conflict
                         Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode);
                     }
                     else
                     {
                         Keep(i.GetDirCacheEntry());
                     }
                 }
                 else
                 {
                     // The index contains a folder
                     Keep(i.GetDirCacheEntry());
                 }
             }
         }
     }
     else
     {
         // There is no entry in the merge commit. Means: we want to delete
         // what's currently in the index and working tree
         if (f != null)
         {
             // There is a file/folder for that path in the working tree
             if (walk.IsDirectoryFileConflict())
             {
                 conflicts.AddItem(walk.PathString);
             }
             else
             {
                 // No file/folder conflict exists. All entries are files or
                 // all entries are folders
                 if (i != null)
                 {
                     // ... and the working tree contained a file or folder
                     // -> add it to the removed set and remove it from
                     // conflicts set
                     Remove(i.EntryPathString);
                     conflicts.Remove(i.EntryPathString);
                 }
             }
         }
         else
         {
             // untracked file, neither contained in tree to merge
             // nor in index
             // There is no file/folder for that path in the working tree.
             // The only entry we have is the index entry. If that entry is a
             // conflict simply remove it. Otherwise keep that entry in the
             // index
             if (i.GetDirCacheEntry().Stage == 0)
             {
                 Keep(i.GetDirCacheEntry());
             }
         }
     }
 }
Example #12
0
		/// <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 &lt;a href=
		/// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
		/// &gt;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;
		}