aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorChristian Halstrick <christian.halstrick@sap.com>2014-07-25 08:41:38 +0200
committerChristian Halstrick <christian.halstrick@sap.com>2014-07-28 14:52:07 +0200
commit3b031fe3dc8c4e0e06e648e281af93533740ec93 (patch)
tree7bf7a46c217b1e1afcae50b903780d6a24fa5509 /org.eclipse.jgit
parent289b7c60a9e232f7b37829c5aad858c7c13286b7 (diff)
downloadjgit-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')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java45
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;
}