diff options
author | Christian Halstrick <christian.halstrick@sap.com> | 2014-07-25 08:41:38 +0200 |
---|---|---|
committer | Christian Halstrick <christian.halstrick@sap.com> | 2014-07-28 14:52:07 +0200 |
commit | 3b031fe3dc8c4e0e06e648e281af93533740ec93 (patch) | |
tree | 7bf7a46c217b1e1afcae50b903780d6a24fa5509 /org.eclipse.jgit | |
parent | 289b7c60a9e232f7b37829c5aad858c7c13286b7 (diff) | |
download | jgit-3b031fe3dc8c4e0e06e648e281af93533740ec93.tar.gz jgit-3b031fe3dc8c4e0e06e648e281af93533740ec93.zip |
RecursiveMerger should not fail on content-merge conflicts of parents
Previously when RecursiveMerger was trying to create a single virtual
common base for the merge it was failing when this lead to content-merge
conflicts. This is different from what native git is doing. When native
git's recursive merge algorithm creates a new common base it will merge
the multiple parents and simply take the merge result (potentially
including conflict markers) as common base. See my discussion with Shawn
here: http://www.spinics.net/lists/git/msg234959.html :
> - How should workingtree, index (stage1,2,3) look like if during
that
> merge of common ancestors a conflict occurs? Will I see in stage2
and
> stage3 really see content of X1 and X2?
Its done entirely in memory and never touches the working tree or
index. When a conflict exists in the X1-X2 merge the conflict is
preserved into the new virtual base.
There is still the possibility that the merge of parents lead to
conflicts. File/Folder conclicts, conflicts on filemodes. This commit
only fixes the situation for conflicts when merging content.
Bug: 438203
Change-Id: If45bc3d078b3d3de87b758e71d7379059d709603
Diffstat (limited to 'org.eclipse.jgit')
3 files changed, 51 insertions, 9 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java index 2dd4426e11..bac2b4fa78 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java @@ -161,4 +161,16 @@ public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> { public boolean containsConflicts() { return containsConflicts; } + + /** + * Sets explicitly whether this merge should be seen as containing a + * conflict or not. Needed because during RecursiveMerger we want to do + * content-merges and take the resulting content (even with conflict + * markers!) as new conflict-free content + * + * @param containsConflicts + */ + protected void setContainsConflicts(boolean containsConflicts) { + this.containsConflicts = containsConflicts; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java index 885d88e5ce..af6c1f9647 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java @@ -196,8 +196,7 @@ public class RecursiveMerger extends ResolveMerger { if (mergeTrees( openTree(getBaseCommit(currentBase, nextBase, callDepth + 1).getTree()), - currentBase.getTree(), - nextBase.getTree())) + currentBase.getTree(), nextBase.getTree(), true)) currentBase = createCommitForTree(resultTree, parents); else throw new NoMergeBaseException( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 28d42a6161..d9cd50def5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -297,7 +297,8 @@ public class ResolveMerger extends ThreeWayMerger { dircache = getRepository().lockDirCache(); try { - return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1]); + return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1], + false); } finally { if (implicitDirCache) dircache.unlock(); @@ -457,6 +458,9 @@ public class ResolveMerger extends ThreeWayMerger { * the index entry * @param work * the file in the working tree + * @param ignoreConflicts + * see + * {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)} * @return <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 @@ -468,7 +472,8 @@ public class ResolveMerger extends ThreeWayMerger { */ protected boolean processEntry(CanonicalTreeParser base, CanonicalTreeParser ours, CanonicalTreeParser theirs, - DirCacheBuildIterator index, WorkingTreeIterator work) + DirCacheBuildIterator index, WorkingTreeIterator work, + boolean ignoreConflicts) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { enterSubtree = true; @@ -627,9 +632,11 @@ public class ResolveMerger extends ThreeWayMerger { } MergeResult<RawText> result = contentMerge(base, ours, theirs); + if (ignoreConflicts) + result.setContainsConflicts(false); File of = writeMergedFile(result); updateIndex(base, ours, theirs, result, of); - if (result.containsConflicts()) + if (result.containsConflicts() && !ignoreConflicts) unmergedPaths.add(tw.getPathString()); modifiedFiles.add(tw.getPathString()); } else if (modeO != modeT) { @@ -993,12 +1000,32 @@ public class ResolveMerger extends ThreeWayMerger { * @param baseTree * @param headTree * @param mergeTree + * @param ignoreConflicts + * Controls what to do in case a content-merge is done and a + * conflict is detected. The default setting for this should be + * <code>false</code>. In this case the working tree file is + * filled with new content (containing conflict markers) and the + * index is filled with multiple stages containing BASE, OURS and + * THEIRS content. Having such non-0 stages is the sign to git + * tools that there are still conflicts for that path. + * <p> + * If <code>true</code> is specified the behavior is different. + * In case a conflict is detected the working tree file is again + * filled with new content (containing conflict markers). But + * also stage 0 of the index is filled with that content. No + * other stages are filled. Means: there is no conflict on that + * path but the new content (including conflict markers) is + * stored as successful merge result. This is needed in the + * context of {@link RecursiveMerger} where when determining + * merge bases we don't want to deal with content-merge + * conflicts. * @return whether the trees merged cleanly * @throws IOException * @since 3.0 */ protected boolean mergeTrees(AbstractTreeIterator baseTree, - RevTree headTree, RevTree mergeTree) throws IOException { + RevTree headTree, RevTree mergeTree, boolean ignoreConflicts) + throws IOException { builder = dircache.builder(); DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder); @@ -1011,7 +1038,7 @@ public class ResolveMerger extends ThreeWayMerger { if (workingTreeIterator != null) tw.addTree(workingTreeIterator); - if (!mergeTreeWalk(tw)) { + if (!mergeTreeWalk(tw, ignoreConflicts)) { return false; } @@ -1050,11 +1077,15 @@ public class ResolveMerger extends ThreeWayMerger { * * @param treeWalk * The walk to iterate over. + * @param ignoreConflicts + * see + * {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)} * @return Whether the trees merged cleanly. * @throws IOException * @since 3.4 */ - protected boolean mergeTreeWalk(TreeWalk treeWalk) throws IOException { + protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts) + throws IOException { boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE; while (treeWalk.next()) { if (!processEntry( @@ -1063,7 +1094,7 @@ public class ResolveMerger extends ThreeWayMerger { treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class), treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class), hasWorkingTreeIterator ? treeWalk.getTree(T_FILE, - WorkingTreeIterator.class) : null)) { + WorkingTreeIterator.class) : null, ignoreConflicts)) { cleanUp(); return false; } |