[514415] Relationships between diffs should be cached

Add an interface for diff relationship computers that are able to
calculate diff relationships and provide two implementations:
- Standard implementation replacing MergeDependenciesUtil methods.
- Caching implementation keeping the results until invalidated.

The computers are usable whenever diff relationships are calculated.
Either an explicit instance is provided or the standard implementation
is used as fallback:
- ComputeDiffsToMerge calculates the order in which diffs are merged.
- BatchMerger propagates to ComputeDiffsToMerge.
- DependencyData finds required/rejected diffs for selected diffs.

The instances are created in two scenarios:
- The resource mapping mergers create their own caching instance and
invalidate the cache after performing their operation.
- The EMF SMV creates its own caching instance for the UI, stored in the
EMFCompareConfiguration and invalidates the cache when another
comparison is used.

Includes tests.

Bug: 514415
Change-Id: Id2a56db00b6ca80db2602b3f207377e72a9d80b8
Signed-off-by: Martin Fleck <mfleck@eclipsesource.com>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java
index 21e3d10..c47d218 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java
@@ -39,6 +39,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeNonConflictingRunnable;
 import org.eclipse.emf.compare.ide.ui.tests.command.data.MergeAllCommandInputData;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
 import org.eclipse.emf.compare.scope.IComparisonScope;
@@ -86,7 +87,7 @@
 				.addAll(ImmutableList.of(leftResource, rightResource, originResource)).build();
 
 		MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry));
 
 		MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers,
 				comparison, leftToRight, mergerRegistry, runnable);
@@ -155,7 +156,7 @@
 				.addAll(ImmutableList.of(leftResource, rightResource, originResource)).build();
 
 		MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry));
 
 		MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers,
 				comparison, leftToRight, mergerRegistry, runnable);
@@ -224,7 +225,7 @@
 				.addAll(ImmutableList.of(leftResource, rightResource, originResource)).build();
 
 		MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry));
 
 		MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers,
 				comparison, leftToRight, mergerRegistry, runnable);
@@ -293,7 +294,7 @@
 				.addAll(ImmutableList.of(leftResource, rightResource, originResource)).build();
 
 		MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry));
 
 		MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers,
 				comparison, leftToRight, mergerRegistry, runnable);
@@ -369,7 +370,7 @@
 				.addAll(ImmutableList.of(leftResource, originResource)).build();
 
 		MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry));
 
 		MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers,
 				comparison, mergeMode.isLeftToRight(isLeftEditable, isRightEditable), mergerRegistry,
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java
index a520551..61f72fe 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java
@@ -33,7 +33,8 @@
 import org.eclipse.emf.compare.ide.ui.tests.framework.RuntimeTestRunner;
 import org.eclipse.emf.compare.ide.ui.tests.framework.annotations.Compare;
 import org.eclipse.emf.compare.ide.ui.tests.framework.internal.CompareTestSupport;
-import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMergeOptionAware;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
@@ -46,7 +47,7 @@
  * 
  * @author Martin Fleck <mfleck@eclipsesource.com>
  */
-@SuppressWarnings({"restriction", "nls" })
+@SuppressWarnings({"nls" })
 @RunWith(RuntimeTestRunner.class)
 public class CascadingFilterRefinementTest {
 
@@ -172,8 +173,8 @@
 	 */
 	public void verifyRefinement(List<Diff> differences, boolean mergeRightToLeft) {
 		for (Diff diff : differences) {
-			Set<Diff> resultingMerges = MergeDependenciesUtil.getAllResultingMerges(diff, MERGER_REGISTRY,
-					mergeRightToLeft);
+			IDiffRelationshipComputer computer = new DiffRelationshipComputer(MERGER_REGISTRY);
+			Set<Diff> resultingMerges = computer.getAllResultingMerges(diff, mergeRightToLeft);
 			assertTrue("Not all refined diffs are in resulting merges.",
 					resultingMerges.containsAll(diff.getRefines()));
 			assertTrue("Not all refining diffs are in resulting merges.",
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java
index e1877bc..e0e07ec 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java
@@ -32,6 +32,7 @@
 import org.eclipse.emf.compare.internal.spec.DiffSpec;
 import org.eclipse.emf.compare.internal.spec.MatchSpec;
 import org.eclipse.emf.compare.merge.AbstractMerger;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.junit.Assert;
 import org.junit.Test;
@@ -570,7 +571,7 @@
 				throw new IllegalArgumentException();
 		}
 		MergeNonConflictingRunnable mergeNonConflicting = new MergeNonConflictingRunnable(isLeftEditable,
-				isRightEditable, mergeMode);
+				isRightEditable, mergeMode, new DiffRelationshipComputer(MERGER_REGISTRY));
 		mergeNonConflicting.merge(comparison, leftToRight, MERGER_REGISTRY);
 	}
 }
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java
index b1d5ad0..c460c19 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java
@@ -30,6 +30,7 @@
 
 import java.util.Iterator;
 
+import org.eclipse.emf.common.notify.Adapter;
 import org.eclipse.emf.common.util.BasicEList;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.Monitor;
@@ -44,6 +45,7 @@
 import org.eclipse.emf.compare.ReferenceChange;
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeNonConflictingRunnable;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMergeCriterion;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
@@ -502,6 +504,7 @@
 		when(diff.getRefinedBy()).thenReturn(new BasicEList<Diff>());
 		when(diff.getRefines()).thenReturn(new BasicEList<Diff>());
 		when(diff.getState()).thenReturn(UNRESOLVED);
+		when(diff.eAdapters()).thenReturn(new BasicEList<Adapter>());
 		return diff;
 	}
 
@@ -634,7 +637,8 @@
 			default:
 				throw new IllegalArgumentException();
 		}
-		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mergeMode) {
+		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mergeMode,
+				new DiffRelationshipComputer(mergerRegistry)) {
 			@Override
 			protected void markAsMerged(Diff diff, MergeMode mode, boolean mergeRightToLeft,
 					Registry registry) {
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java
index 953cc12..e881dae 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java
@@ -28,6 +28,7 @@
 import org.eclipse.emf.compare.EMFCompare;
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl;
 import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
@@ -112,7 +113,8 @@
 	public void testMergeDataAfterAcceptingDeletion() {
 		ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		// Assert merge data and diff states
@@ -131,7 +133,8 @@
 		// filter activated).
 		ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete, leftMove);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		// Assert merge data and diff states
@@ -152,7 +155,8 @@
 		 */
 		ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		// Assert merge data and diff states
@@ -169,7 +173,8 @@
 		// Mocks the UI behavior of UI if the movement diff is selected for merging.
 		ArrayList<Diff> uiDiff = Lists.newArrayList(leftMove);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		// Assert merge data
@@ -187,7 +192,8 @@
 		// Mocks the UI behavior of UI if the movement diff is selected for merging.
 		ArrayList<Diff> uiDiff = Lists.newArrayList(leftMove);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		// Assert merge data
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java
index 9f74b0e..1be009a 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java
@@ -32,6 +32,7 @@
 import org.eclipse.emf.compare.internal.merge.IMergeData;
 import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
 import org.eclipse.emf.compare.internal.spec.ReferenceChangeSpec;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
@@ -133,7 +134,8 @@
 		// The subDiff is not added in this list because it is not considered as a cascading diff.
 		ArrayList<Diff> uiDiff = Lists.newArrayList(deletionDiff);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		Node rootNode = (Node)left.getContents().get(0);
@@ -168,7 +170,8 @@
 		// The subDiff is not added in this list because it is not considered as a cascading diff.
 		ArrayList<Diff> uiDiff = Lists.newArrayList(deletionDiff);
 
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(uiDiff, false, mergerRegistry);
 
 		Node rootNode = (Node)left.getContents().get(0);
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java
index e4af7d6..89bdd09 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java
@@ -30,6 +30,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl;
 import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
 import org.eclipse.emf.compare.merge.BatchMerger;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
@@ -126,7 +127,8 @@
 	 */
 	@Test
 	public void testAcceptConflictDiffWithConflictingDiffWithRequiredBy() {
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(Collections.singletonList(refChangeDiff), false, mergerRegistry);
 
 		assertEquals(MERGED, refChangeDiff.getState());
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java
index 32c4d83..d92e868 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java
@@ -30,6 +30,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl;
 import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
 import org.eclipse.emf.compare.merge.BatchMerger;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
@@ -127,7 +128,8 @@
 	 */
 	@Test
 	public void testAcceptConflictDiffWithConflictingDiffWithRequiredBy() {
-		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT);
+		MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT,
+				new DiffRelationshipComputer(mergerRegistry));
 		mergeRunnable.merge(Collections.singletonList(refChangeDiff), false, mergerRegistry);
 
 		assertEquals(MERGED, refChangeDiff.getState());
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java
index cefe226..38969b3 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2016 Obeo and others.
+ * Copyright (c) 2013, 2017 Obeo and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *     Obeo - initial API and implementation
  *     Conor O'Mahony - bug 507465
  *     Martin Fleck - bug 483798
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.configuration;
 
@@ -29,11 +30,15 @@
 import org.eclipse.emf.compare.domain.ICompareEditingDomain;
 import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.rcp.ui.EMFCompareRCPUIPlugin;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.AdapterFactoryChange;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.CompareEditingDomainChange;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.ComparisonAndScopeChange;
+import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.DiffRelationshipComputerChange;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.EMFComparatorChange;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.MergePreviewModeChange;
 import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter;
@@ -61,6 +66,9 @@
 
 	private static final String ADAPTER_FACTORY = EMFCompareIDEUIPlugin.PLUGIN_ID + ".ADAPTER_FACTORY"; //$NON-NLS-1$
 
+	private static final String DIFF_RELATIONSHIP_COMPUTER = EMFCompareIDEUIPlugin.PLUGIN_ID
+			+ ".DIFF_RELATIONSHIP_COMPUTER"; //$NON-NLS-1$
+
 	private static final String PREVIEW_MERGE_MODE = EMFCompareIDEUIPlugin.PLUGIN_ID + ".PREVIEW_MERGE_MODE"; //$NON-NLS-1$
 
 	private static final String COMPARISON_SCOPE = EMFCompareIDEUIPlugin.PLUGIN_ID + ".COMPARISON_SCOPE"; //$NON-NLS-1$ ;
@@ -125,6 +133,11 @@
 		if (getProperty(DISPLAY_SAVE_ACTION) == null) {
 			setProperty(DISPLAY_SAVE_ACTION, Boolean.TRUE);
 		}
+
+		if (getProperty(DIFF_RELATIONSHIP_COMPUTER) == null) {
+			setProperty(DIFF_RELATIONSHIP_COMPUTER,
+					new DiffRelationshipComputer(EMFCompareRCPPlugin.getDefault().getMergerRegistry()));
+		}
 	}
 
 	/**
@@ -163,6 +176,7 @@
 		compareConfiguration.setProperty(SMV_FILTERS, null);
 		compareConfiguration.setProperty(EDITING_DOMAIN, null);
 		compareConfiguration.setProperty(ADAPTER_FACTORY, null);
+		compareConfiguration.setProperty(DIFF_RELATIONSHIP_COMPUTER, null);
 		compareConfiguration.setProperty(SMV_GROUP_PROVIDERS, null);
 		compareConfiguration.setProperty(PREVIEW_MERGE_MODE, null);
 		compareConfiguration.setProperty(DISPLAY_GROUP_PROVIDERS, null);
@@ -201,6 +215,10 @@
 		return (AdapterFactory)getProperty(ADAPTER_FACTORY);
 	}
 
+	public IDiffRelationshipComputer getDiffRelationshipComputer() {
+		return (IDiffRelationshipComputer)getProperty(DIFF_RELATIONSHIP_COMPUTER);
+	}
+
 	/**
 	 * {@inheritDoc}
 	 * 
@@ -293,6 +311,12 @@
 		getEventBus().post(new AdapterFactoryChange(oldValue, adapterFactory));
 	}
 
+	public void setDiffRelationshipComputer(IDiffRelationshipComputer diffRelationshipComputer) {
+		IDiffRelationshipComputer oldValue = getDiffRelationshipComputer();
+		setProperty(DIFF_RELATIONSHIP_COMPUTER, diffRelationshipComputer);
+		getEventBus().post(new DiffRelationshipComputerChange(oldValue, diffRelationshipComputer));
+	}
+
 	private class PropertyChangeListener implements IPropertyChangeListener {
 		public void propertyChange(PropertyChangeEvent event) {
 			fireChange(event.getProperty(), event.getOldValue(), event.getNewValue());
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java
index 53f2866..dde9c88 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2017 Obeo.
+ * Copyright (c) 2016, 2017 Obeo and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  * Contributors:
  *     Obeo - initial API and implementation
  *     Martin Fleck - bug 512562
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.logical;
 
@@ -60,6 +61,7 @@
 import org.eclipse.emf.compare.ide.utils.ResourceUtil;
 import org.eclipse.emf.compare.internal.utils.DiffUtil;
 import org.eclipse.emf.compare.merge.AdditiveMergeCriterion;
+import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.ComputeDiffsToMerge;
 import org.eclipse.emf.compare.merge.DelegatingMerger;
 import org.eclipse.emf.compare.merge.MergeBlockedByConflictException;
@@ -111,11 +113,14 @@
 	private Set<URI> performPreMerge(Comparison comparison, SubMonitor subMonitor) {
 		final Monitor emfMonitor = BasicMonitor.toMonitor(subMonitor);
 		final Set<URI> conflictingURIs = new LinkedHashSet<URI>();
-		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, MERGER_REGISTRY,
-				AdditiveMergeCriterion.INSTANCE).failOnRealConflictUnless(isAdditiveConflict());
+		CachingDiffRelationshipComputer relationshipComputer = new CachingDiffRelationshipComputer(
+				MERGER_REGISTRY, AdditiveMergeCriterion.INSTANCE);
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer)
+				.failOnRealConflictUnless(isAdditiveConflict());
 		for (Diff next : comparison.getDifferences()) {
 			doMergeForDiff(next, computer, emfMonitor, conflictingURIs);
 		}
+		relationshipComputer.invalidate();
 		return conflictingURIs;
 	}
 
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
index 788272b..2078dcc 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
@@ -11,6 +11,7 @@
  *     Alexandra Buzila - bugs 470332, 478620
  *     Stefan Dirix - bug 507050
  *     Martin Fleck - bug 512562, 514382
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.logical;
 
@@ -76,8 +77,10 @@
 import org.eclipse.emf.compare.ide.utils.ResourceUtil;
 import org.eclipse.emf.compare.ide.utils.StorageTraversal;
 import org.eclipse.emf.compare.merge.BatchMerger;
+import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.ComputeDiffsToMerge;
 import org.eclipse.emf.compare.merge.IBatchMerger;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.merge.IMerger.Registry2;
 import org.eclipse.emf.compare.merge.MergeBlockedByConflictException;
@@ -325,15 +328,19 @@
 	 */
 	private Set<URI> performPreMerge(Comparison comparison, SubMonitor subMonitor) {
 		final Monitor emfMonitor = BasicMonitor.toMonitor(subMonitor);
+		final CachingDiffRelationshipComputer relationshipComputer = new CachingDiffRelationshipComputer(
+				MERGER_REGISTRY);
 		final Set<URI> conflictingURIs = new LinkedHashSet<URI>();
 		for (Diff next : filter(comparison.getDifferences(), fromSide(DifferenceSource.RIGHT))) {
-			doMergeForDiff(emfMonitor, conflictingURIs, next);
+			doMergeForDiff(emfMonitor, conflictingURIs, next, relationshipComputer);
 		}
+		relationshipComputer.invalidate();
 		return conflictingURIs;
 	}
 
-	protected void doMergeForDiff(Monitor emfMonitor, Set<URI> conflictingURIs, Diff diff) {
-		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, MERGER_REGISTRY)
+	protected void doMergeForDiff(Monitor emfMonitor, Set<URI> conflictingURIs, Diff diff,
+			IDiffRelationshipComputer relationshipComputer) {
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer)
 				.failOnRealConflictUnless(alwaysFalse());
 		try {
 			Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff);
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
index 59d98d2..2ad486e 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
@@ -8,6 +8,7 @@
  * Contributors:
  *     Obeo - initial API and implementation
  *     Martin Fleck - bug 514767
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer;
 
@@ -27,8 +28,9 @@
 import java.util.Set;
 
 import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
 import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider;
@@ -55,6 +57,24 @@
 	}
 
 	/**
+	 * Returns the diff relationship computer instance from the compare configuration with the given merger
+	 * registry. If no computer instance has been set, a default instance will be created.
+	 * 
+	 * @param mergerRegistry
+	 *            merger registry used to compute diff relationships.
+	 * @return a non-null diff relationship computer.
+	 */
+	protected IDiffRelationshipComputer getDiffRelationshipComputer(IMerger.Registry mergerRegistry) {
+		if (compareConfiguration == null || compareConfiguration.getDiffRelationshipComputer() == null) {
+			return new DiffRelationshipComputer(mergerRegistry);
+		}
+		IDiffRelationshipComputer diffRelationshipComputer = compareConfiguration
+				.getDiffRelationshipComputer();
+		diffRelationshipComputer.setMergerRegistry(mergerRegistry);
+		return diffRelationshipComputer;
+	}
+
+	/**
 	 * @param selection
 	 */
 	public void updateDependencies(ISelection selection, IMerger.Registry mergerRegistry) {
@@ -69,11 +89,10 @@
 			rejectedDiffs = newHashSet();
 			for (Diff diff : selectedDiffs) {
 				boolean leftToRight = mergePreviewMode.isLeftToRight(diff, leftEditable, rightEditable);
-				requires.addAll(
-						MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry, !leftToRight));
+				IDiffRelationshipComputer computer = getDiffRelationshipComputer(mergerRegistry);
+				requires.addAll(computer.getAllResultingMerges(diff, !leftToRight));
 				requires.remove(diff);
-				rejectedDiffs.addAll(
-						MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry, !leftToRight));
+				rejectedDiffs.addAll(computer.getAllResultingRejections(diff, !leftToRight));
 				rejectedDiffs.remove(diff);
 				requires.removeAll(rejectedDiffs);
 			}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
index 3538a3b..db93e11 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
@@ -13,6 +13,7 @@
  *     Martin Fleck - bug 497066
  *     Martin Fleck - bug 483798
  *     Martin Fleck - bug 514767
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer;
 
@@ -117,6 +118,7 @@
 import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
 import org.eclipse.emf.compare.merge.AbstractMerger;
+import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMergeOptionAware;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
@@ -240,6 +242,9 @@
 	/** The adapter factory. */
 	private ComposedAdapterFactory fAdapterFactory;
 
+	/** The diff relationship computer. */
+	private CachingDiffRelationshipComputer fDiffRelationshipComputer;
+
 	/** The tree ruler associated with this viewer. */
 	private EMFCompareDiffTreeRuler treeRuler;
 
@@ -579,6 +584,10 @@
 		fAdapterFactory = initAdapterFactory(getCompareConfiguration().getComparison());
 		getCompareConfiguration().setAdapterFactory(fAdapterFactory);
 
+		fDiffRelationshipComputer = new CachingDiffRelationshipComputer(
+				EMFCompareRCPPlugin.getDefault().getMergerRegistry());
+		getCompareConfiguration().setDiffRelationshipComputer(fDiffRelationshipComputer);
+
 		inputChangedTask = new CompareInputChangedJob(EMFCompareIDEUIMessages
 				.getString("EMFCompareStructureMergeViewer.computingModelDifferences")); //$NON-NLS-1$
 	}
@@ -934,6 +943,7 @@
 		compareInputChanged((ICompareInput)null);
 		treeRuler.handleDispose();
 		fAdapterFactory.dispose();
+		fDiffRelationshipComputer.invalidate();
 		toolBar.dispose();
 		fColors.dispose();
 
@@ -1089,6 +1099,11 @@
 			}
 			fAdapterFactory = initAdapterFactory(comparison);
 
+			// clear cache for new comparison
+			if (fDiffRelationshipComputer != null) {
+				fDiffRelationshipComputer.invalidate();
+			}
+
 			// propagate new adapter factory
 			config.setAdapterFactory(fAdapterFactory);
 			getContentProvider().setAdapterFactory(fAdapterFactory);
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java
index cdec275..9c0dd73 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2017 Obeo.
+ * Copyright (c) 2014, 2017 Obeo and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  * 
  * Contributors:
  *     Obeo - initial API and implementation
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
 
@@ -21,8 +22,10 @@
 import java.util.Set;
 
 import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
 
 /**
@@ -31,6 +34,7 @@
  * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
  */
 public abstract class AbstractMergeRunnable {
+
 	/** Tells us whether the left side of the comparison we're operating on is editable. */
 	private final boolean isLeftEditable;
 
@@ -40,6 +44,9 @@
 	/** Current merging mode. */
 	private final MergeMode mergeMode;
 
+	/** Computer to calculate the relationship between diffs. */
+	private IDiffRelationshipComputer diffRelationshipComputer;
+
 	/**
 	 * Default constructor.
 	 * 
@@ -49,11 +56,15 @@
 	 *            Whether the right side of the comparison we're operating on is editable.
 	 * @param mergeMode
 	 *            Merge mode for this operation.
+	 * @param diffRelationshipComputer
+	 *            The diff relationship computer used to find resulting merges and rejections.
 	 */
-	public AbstractMergeRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) {
+	public AbstractMergeRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode,
+			IDiffRelationshipComputer diffRelationshipComputer) {
 		this.isLeftEditable = isLeftEditable;
 		this.isRightEditable = isRightEditable;
 		this.mergeMode = mergeMode;
+		this.diffRelationshipComputer = diffRelationshipComputer;
 	}
 
 	protected boolean isLeftEditable() {
@@ -69,6 +80,22 @@
 	}
 
 	/**
+	 * Returns the diff relationship computer instance from the compare configuration with the given merger
+	 * registry. If no computer instance has been set, a default instance will be created.
+	 * 
+	 * @param mergerRegistry
+	 *            merger registry used to compute diff relationships.
+	 * @return a non-null diff relationship computer.
+	 */
+	protected IDiffRelationshipComputer getDiffRelationshipComputer(IMerger.Registry mergerRegistry) {
+		if (diffRelationshipComputer == null) {
+			diffRelationshipComputer = new DiffRelationshipComputer(mergerRegistry);
+		}
+		diffRelationshipComputer.setMergerRegistry(mergerRegistry);
+		return diffRelationshipComputer;
+	}
+
+	/**
 	 * Marks all of the given diffs as merged, keeping track of the merged mode used for the operation.
 	 * 
 	 * @param diffToMarkAsMerged
@@ -81,7 +108,7 @@
 	protected void markAllAsMerged(Collection<? extends Diff> diffToMarkAsMerged, MergeMode mode,
 			Registry mergerRegistry) {
 		for (Diff diff : diffToMarkAsMerged) {
-			boolean isLeftToRight = mode.isLeftToRight(diff, isLeftEditable, isRightEditable);
+			boolean isLeftToRight = mode.isLeftToRight(diff, isLeftEditable(), isRightEditable());
 			markAsMerged(diff, mode, !isLeftToRight, mergerRegistry);
 		}
 	}
@@ -103,11 +130,10 @@
 		if (isInTerminalState(diff)) {
 			return;
 		}
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer(mergerRegistry);
 		if (isAccepting(diff, mergeRightToLeft)) {
-			final Set<Diff> implied = MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry,
-					mergeRightToLeft);
-			final Set<Diff> rejections = MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry,
-					mergeRightToLeft);
+			final Set<Diff> implied = computer.getAllResultingMerges(diff, mergeRightToLeft);
+			final Set<Diff> rejections = computer.getAllResultingRejections(diff, mergeRightToLeft);
 			for (Diff impliedDiff : Sets.difference(implied, rejections)) {
 				impliedDiff.setState(MERGED);
 			}
@@ -115,8 +141,7 @@
 				impliedRejection.setState(DISCARDED);
 			}
 		} else {
-			final Set<Diff> implied = MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry,
-					mergeRightToLeft);
+			final Set<Diff> implied = computer.getAllResultingMerges(diff, mergeRightToLeft);
 			for (Diff impliedDiff : implied) {
 				impliedDiff.setState(DISCARDED);
 			}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java
index c8b8828..9b1c7ac 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java
@@ -34,6 +34,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
 import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.Navigatable;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
 import org.eclipse.emf.compare.provider.ITooltipLabelProvider;
@@ -81,6 +82,8 @@
 	 */
 	private AdapterFactory adapterFactory;
 
+	private IDiffRelationshipComputer diffRelationshipComputer;
+
 	private boolean isMirrored;
 
 	private final boolean isLeftEditable;
@@ -98,6 +101,7 @@
 		super(""); //$NON-NLS-1$
 
 		adapterFactory = compareConfiguration.getAdapterFactory();
+		diffRelationshipComputer = compareConfiguration.getDiffRelationshipComputer();
 		isLeftEditable = compareConfiguration.isLeftEditable();
 		isRightEditable = compareConfiguration.isRightEditable();
 
@@ -118,7 +122,8 @@
 		this.editingDomain = compareConfiguration.getEditingDomain();
 		this.mergerRegistry = mergerRegistry;
 		this.leftToRight = mode.isLeftToRight(isLeftEditable, isRightEditable);
-		this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable);
+		this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable,
+				diffRelationshipComputer);
 		this.selectedDifferences = newArrayList();
 		this.selectedMode = mode;
 
@@ -131,9 +136,9 @@
 		updateSelection(selection);
 	}
 
-	protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable,
-			boolean rightEditable) {
-		return new MergeRunnableImpl(leftEditable, rightEditable, mode);
+	protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable, boolean rightEditable,
+			IDiffRelationshipComputer relationshipComputer) {
+		return new MergeRunnableImpl(leftEditable, rightEditable, mode, relationshipComputer);
 	}
 
 	protected void initToolTipAndImage(MergeMode mode) {
@@ -300,10 +305,12 @@
 			if (mirrored) {
 				MergeMode mirroredMode = selectedMode.inverse();
 				leftToRight = mirroredMode.isLeftToRight(isRightEditable, isLeftEditable);
-				mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable);
+				mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable,
+						diffRelationshipComputer);
 			} else {
 				leftToRight = selectedMode.isLeftToRight(isLeftEditable, isRightEditable);
-				mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable);
+				mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable,
+						diffRelationshipComputer);
 			}
 		}
 	}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java
index ed8b885..16a7e5b 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2015 Obeo.
+ * Copyright (c) 2013, 2017 Obeo.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  * Contributors:
  *     Obeo - initial API and implementation
  *     Martin Fleck - bug 514079
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
 
@@ -21,6 +22,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
 import org.eclipse.emf.compare.internal.domain.IMergeAllNonConflictingRunnable;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
 import org.eclipse.jface.viewers.IStructuredSelection;
@@ -50,8 +52,8 @@
 
 	@Override
 	protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean isLeftEditable,
-			boolean isRightEditable) {
-		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode);
+			boolean isRightEditable, IDiffRelationshipComputer relationshipComputer) {
+		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode, relationshipComputer);
 	}
 
 	@Override
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java
index 89152d7..15135fa 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 EclipseSource Munich and others.
+ * Copyright (c) 2015, 2017 EclipseSource Munich and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  * 
  * Contributors:
  *     Philip Langer - initial API and implementation
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
 
@@ -30,6 +31,7 @@
 import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
 import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
 import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
 import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup;
@@ -124,8 +126,8 @@
 
 	@Override
 	protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean isLeftEditable,
-			boolean isRightEditable) {
-		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode);
+			boolean isRightEditable, IDiffRelationshipComputer relationshipComputer) {
+		return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode, relationshipComputer);
 	}
 
 	@Override
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java
index de5972c..445f452 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java
@@ -9,6 +9,7 @@
  *     Obeo - initial API and implementation
  *     Philip Langer - bug 469355, bug 462884, refactorings
  *     Martin Fleck - bug 507177
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
 
@@ -53,9 +54,9 @@
 import org.eclipse.emf.compare.merge.BatchMerger;
 import org.eclipse.emf.compare.merge.ComputeDiffsToMerge;
 import org.eclipse.emf.compare.merge.IBatchMerger;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
-import org.eclipse.emf.compare.merge.IMerger.Registry2;
 import org.eclipse.emf.compare.merge.MergeBlockedByConflictException;
 
 /**
@@ -77,9 +78,12 @@
 	 *            Whether the right side of the comparison we're operating on is editable.
 	 * @param mergeMode
 	 *            Merge mode for this operation.
+	 * @param diffRelationshipComputer
+	 *            The diff relationship computer used to find resulting merges and rejections.
 	 */
-	public MergeNonConflictingRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) {
-		super(isLeftEditable, isRightEditable, mergeMode);
+	public MergeNonConflictingRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode,
+			IDiffRelationshipComputer diffRelationshipComputer) {
+		super(isLeftEditable, isRightEditable, mergeMode, diffRelationshipComputer);
 	}
 
 	/**
@@ -151,7 +155,7 @@
 	private Iterable<Diff> mergeThreeWayWithoutConflicts(Collection<Diff> differences, boolean leftToRight,
 			Registry mergerRegistry) {
 		final List<Diff> affectedDiffs;
-		final IBatchMerger merger = new BatchMerger(mergerRegistry);
+		final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry));
 
 		if (getMergeMode() == MergeMode.LEFT_TO_RIGHT) {
 			affectedDiffs = Lists
@@ -199,7 +203,7 @@
 	private Iterable<Diff> mergeTwoWay(Collection<Diff> differences, boolean leftToRight,
 			Registry mergerRegistry) {
 		final List<Diff> affectedDiffs;
-		final IBatchMerger merger = new BatchMerger(mergerRegistry);
+		final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry));
 
 		// in two-way comparison, difference source is always LEFT
 		if (getMergeMode() == MergeMode.LEFT_TO_RIGHT) {
@@ -234,8 +238,8 @@
 			Registry mergerRegistry) {
 		final List<Diff> affectedDiffs = new ArrayList<Diff>();
 		final Monitor emfMonitor = new BasicMonitor();
-		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(!leftToRight, (Registry2)mergerRegistry)
-				.failOnRealConflictUnless(alwaysFalse());
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(!leftToRight,
+				getDiffRelationshipComputer(mergerRegistry)).failOnRealConflictUnless(alwaysFalse());
 
 		final Predicate<? super Diff> filter;
 		if (getMergeMode() == RIGHT_TO_LEFT) {
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
index f692550..234b7ff 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2017 Obeo.
+ * Copyright (c) 2013, 2017 Obeo and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  * 
  * Contributors:
  *     Obeo - initial API and implementation
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
 
@@ -24,6 +25,7 @@
 import org.eclipse.emf.compare.internal.merge.MergeOperation;
 import org.eclipse.emf.compare.merge.BatchMerger;
 import org.eclipse.emf.compare.merge.IBatchMerger;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger.Registry;
 
 /**
@@ -33,8 +35,9 @@
  */
 // Visible for testing
 public final class MergeRunnableImpl extends AbstractMergeRunnable implements IMergeRunnable {
-	public MergeRunnableImpl(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) {
-		super(isLeftEditable, isRightEditable, mergeMode);
+	public MergeRunnableImpl(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode,
+			IDiffRelationshipComputer diffRelationshipComputer) {
+		super(isLeftEditable, isRightEditable, mergeMode, diffRelationshipComputer);
 	}
 
 	public void merge(List<? extends Diff> differences, boolean leftToRight, Registry mergerRegistry) {
@@ -70,7 +73,7 @@
 
 	private void mergeAll(Collection<? extends Diff> differences, boolean leftToRight,
 			Registry mergerRegistry) {
-		final IBatchMerger merger = new BatchMerger(mergerRegistry);
+		final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry));
 		if (leftToRight) {
 			merger.copyAllLeftToRight(differences, new BasicMonitor());
 		} else {
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java
new file mode 100644
index 0000000..05ea629
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.rcp.ui.internal.configuration;
+
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.rcp.ui.configuration.ICompareEvent;
+
+/**
+ * Event indicating a change in {@link IDiffRelationshipComputer}.
+ * 
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ */
+public interface IDiffRelationshipComputerChange extends ICompareEvent {
+
+	/**
+	 * Previous value of the {@link IDiffRelationshipComputer}.
+	 * 
+	 * @return previous value of the {@link IDiffRelationshipComputer}.
+	 */
+	IDiffRelationshipComputer getOldValue();
+
+	/**
+	 * New value of the {@link IDiffRelationshipComputer}.
+	 * 
+	 * @return new value of the {@link IDiffRelationshipComputer}.
+	 */
+	IDiffRelationshipComputer getNewValue();
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java
index c14d27b..6beff7e 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java
@@ -17,6 +17,7 @@
 import org.eclipse.emf.compare.EMFCompare;
 import org.eclipse.emf.compare.domain.ICompareEditingDomain;
 import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter;
 import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.StructureMergeViewerGrouper;
 import org.eclipse.emf.compare.scope.IComparisonScope;
@@ -58,5 +59,9 @@
 
 	void setMergePreviewMode(MergeMode mergePreviewMode);
 
+	IDiffRelationshipComputer getDiffRelationshipComputer();
+
+	void setDiffRelationshipComputer(IDiffRelationshipComputer diffRelationshipComputer);
+
 	boolean getBooleanProperty(String key, boolean dflt);
 }
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java
new file mode 100644
index 0000000..302f47d
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.rcp.ui.internal.configuration.impl;
+
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.rcp.ui.internal.configuration.IDiffRelationshipComputerChange;
+
+/**
+ * A change for {@link IDiffRelationshipComputer}.
+ * 
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ */
+public class DiffRelationshipComputerChange extends CompareEvent<IDiffRelationshipComputer> implements IDiffRelationshipComputerChange {
+	public DiffRelationshipComputerChange(IDiffRelationshipComputer oldValue,
+			IDiffRelationshipComputer newValue) {
+		super(oldValue, newValue);
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java
index 872a55d..e0e6d40 100644
--- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java
@@ -7,10 +7,10 @@
 import java.util.Set;
 
 import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
 import org.eclipse.emf.compare.merge.IMerger;
 
-@SuppressWarnings("restriction")
 public class MergeDependenciesChecker {
 
 	private IMerger.Registry registry;
@@ -58,10 +58,9 @@
 	}
 
 	public void check() {
-		Set<Diff> allResultingMerges = MergeDependenciesUtil.getAllResultingMerges(diff, registry,
-				this.rightToLeft);
-		Set<Diff> allResultingRejections = MergeDependenciesUtil.getAllResultingRejections(diff, registry,
-				this.rightToLeft);
+		IDiffRelationshipComputer computer = new DiffRelationshipComputer(registry);
+		Set<Diff> allResultingMerges = computer.getAllResultingMerges(diff, this.rightToLeft);
+		Set<Diff> allResultingRejections = computer.getAllResultingRejections(diff, this.rightToLeft);
 		assertEquals(this.nbMerges, Sets.difference(allResultingMerges, allResultingRejections).size());
 		assertEquals(this.nbRejections, allResultingRejections.size());
 	}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java
new file mode 100644
index 0000000..252f30c
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java
@@ -0,0 +1,420 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.tests.diff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+import org.eclipse.emf.compare.CompareFactory;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.internal.spec.DiffSpec;
+import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.ComputeDiffsToMerge;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IMerger;
+import org.eclipse.emf.compare.merge.IMerger.Registry2;
+import org.eclipse.emf.compare.merge.IMerger2;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * This class holds test cases that are related to the {@link IDiffRelationshipComputer} and specifically, the
+ * {@link CachingDiffRelationshipComputer}.
+ * 
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ */
+@SuppressWarnings({"boxing" })
+public class DiffRelationshipComputerTest {
+
+	/** Merge direction. */
+	protected static boolean MERGE_RIGHT_TO_LEFT = true;
+
+	/** Merger registry with the mock merger. */
+	protected Registry2 mergerRegistry;
+
+	/** Mock merger with a high ranking applying to the testing diff. */
+	protected IMerger2 merger;
+
+	/** Comparison. */
+	protected Comparison comparison;
+
+	/** Match. */
+	protected Match match;
+
+	/** Testing diff. */
+	protected Diff diff;
+
+	/** Dependency of the testing diff. */
+	protected Diff mergeDependency;
+
+	/** Resulting diff of the testing diff. */
+	protected Diff resultingMerge;
+
+	/** Resulting rejection of the testing diff. */
+	protected Diff resultingRejection;
+
+	/**
+	 * Creates a new merger registry, merger and relevant diffs.
+	 */
+	@Before
+	public void setupClass() {
+		mergerRegistry = createMergerRegistry();
+		comparison = createComparison();
+		comparison.getMatches().add(match = createMatch());
+		comparison.getDifferences().add(diff = createDiff(match));
+		comparison.getDifferences().add(mergeDependency = createDiff(match));
+		comparison.getDifferences().add(resultingMerge = createDiff(match));
+		comparison.getDifferences().add(resultingRejection = createDiff(match));
+		merger = mockMerger(diff, mergeDependency, resultingMerge, resultingRejection, 1000, mergerRegistry);
+		mergerRegistry.add(merger);
+	}
+
+	protected CachingDiffRelationshipComputer getDiffRelationshipComputer() {
+		return new CachingDiffRelationshipComputer(mergerRegistry);
+	}
+
+	/**
+	 * Tests that the computer is working correctly for direct merge dependencies.
+	 */
+	@Test
+	public void testDirectMergeDependencies() {
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// call calculation method a few times
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called exactly once
+		verifyDirectMergeDependenciesCalledExactly(1);
+	}
+
+	/**
+	 * Tests that the computer is working correctly for direct resulting merges.
+	 */
+	@Test
+	public void testDirectResultingMerges() {
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// call calculation method a few times
+		assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called exactly once
+		verifyDirectResultingMergesCalledExactly(1);
+	}
+
+	/**
+	 * Tests that the computer is working correctly for direct resulting rejections.
+	 */
+	@Test
+	public void testDirectResultingRejections() {
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// call calculation method a few times
+		assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called exactly once
+		verifyDirectResultingRejectionsCalledExactly(1);
+	}
+
+	/**
+	 * Tests that the computer is working correctly for all resulting merges.
+	 */
+	@Test
+	public void testAllResultingMerges() {
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// call calculation method a few times
+		assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods of the diffs are called at most once
+		verifyMergerCalculationsCalledAtMostOnce();
+	}
+
+	/**
+	 * Tests that the computer is working correctly for all resulting rejections.
+	 */
+	@Test
+	public void testAllResultingRejections() {
+		IDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// call resulting rejections a few more times
+		assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+		assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods of the diffs are called at most once
+		verifyMergerCalculationsCalledAtMostOnce();
+	}
+
+	/**
+	 * Tests that a diff state change invalidates the cached relationships and leads to a re-calculation of
+	 * the relationships.
+	 */
+	@Test
+	public void testInvalidateCache() {
+		CachingDiffRelationshipComputer computer = getDiffRelationshipComputer();
+
+		// trigger first time caching
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation method was called exactly once
+		verifyDirectMergeDependenciesCalledExactly(1);
+
+		// invalidate cache
+		computer.invalidate();
+
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called twice now, because we needed to recalculate the relationships
+		verifyDirectMergeDependenciesCalledExactly(2);
+
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called twice now, same as before
+		verifyDirectMergeDependenciesCalledExactly(2);
+
+		// invalidate cache
+		computer.invalidate();
+
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+		assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT));
+
+		// the calculation methods was called three times now, because of recalculation
+		verifyDirectMergeDependenciesCalledExactly(3);
+	}
+
+	/**
+	 * Tests that the is correctly used in {@link ComputeDiffsToMerge}.
+	 */
+	@Test
+	public void testComputeDiffsToMergeIntegration() {
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(MERGE_RIGHT_TO_LEFT,
+				new CachingDiffRelationshipComputer(mergerRegistry));
+
+		// call resulting merges a few more times
+		assertAllResultingMerges(computer.getAllDiffsToMerge(diff));
+		assertAllResultingMerges(computer.getAllDiffsToMerge(diff));
+		assertAllResultingMerges(computer.getAllDiffsToMerge(diff));
+		assertAllResultingMerges(computer.getAllDiffsToMerge(diff));
+
+		// the calculation methods of the diffs are called at most once
+		verifyMergerCalculationsCalledAtMostOnce();
+	}
+
+	/**
+	 * Verifies that any of the relationship calculating methods of the merger were called at most once.
+	 */
+	protected void verifyMergerCalculationsCalledAtMostOnce() {
+		verify(merger, atMost(1)).getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT);
+		verify(merger, atMost(1)).getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT);
+		verify(merger, atMost(1)).getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT);
+	}
+
+	/**
+	 * Verifies that the calculation method for the direct merge dependencies was called exactly the given
+	 * number of times.
+	 * 
+	 * @param times
+	 *            number of times the method should have been called.
+	 */
+	protected void verifyDirectMergeDependenciesCalledExactly(int times) {
+		verify(merger, times(times)).getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT);
+	}
+
+	/**
+	 * Verifies that the calculation method for the direct resulting merges was called exactly the given
+	 * number of times.
+	 * 
+	 * @param times
+	 *            number of times the method should have been called.
+	 */
+	protected void verifyDirectResultingMergesCalledExactly(int times) {
+		verify(merger, times(times)).getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT);
+	}
+
+	/**
+	 * Verifies that the calculation method for the direct resulting rejections was called exactly the given
+	 * number of times.
+	 * 
+	 * @param times
+	 *            number of times the method should have been called.
+	 */
+	protected void verifyDirectResultingRejectionsCalledExactly(int times) {
+		verify(merger, times(times)).getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT);
+	}
+
+	/**
+	 * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only
+	 * diffs available in the given set.
+	 * 
+	 * @param resultingMerges
+	 *            calculated resulting merges
+	 */
+	protected void assertDirectMergeDependencies(Set<Diff> mergeDependencies) {
+		assertEquals(1, mergeDependencies.size());
+		assertTrue(mergeDependencies.contains(mergeDependency));
+	}
+
+	/**
+	 * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only
+	 * diffs available in the given set.
+	 * 
+	 * @param resultingMerges
+	 *            calculated resulting merges
+	 */
+	protected void assertDirectResultingRejections(Set<Diff> resultingRejections) {
+		assertEquals(1, resultingRejections.size());
+		assertTrue(resultingRejections.contains(resultingRejection));
+	}
+
+	/**
+	 * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only
+	 * diffs available in the given set.
+	 * 
+	 * @param resultingMerges
+	 *            calculated resulting merges
+	 */
+	protected void assertDirectResultingMerges(Set<Diff> resultingMerges) {
+		assertEquals(1, resultingMerges.size());
+		assertTrue(resultingMerges.contains(resultingMerge));
+	}
+
+	/**
+	 * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only
+	 * diffs available in the given set.
+	 * 
+	 * @param resultingMerges
+	 *            calculated resulting merges
+	 */
+	protected void assertAllResultingMerges(Set<Diff> resultingMerges) {
+		assertEquals(3, resultingMerges.size());
+		assertTrue(resultingMerges.contains(mergeDependency));
+		assertTrue(resultingMerges.contains(diff));
+		assertTrue(resultingMerges.contains(resultingMerge));
+	}
+
+	/**
+	 * Asserts that the known resulting rejection is the only diff available in the given set.
+	 * 
+	 * @param resultingRejections
+	 *            calculated resulting rejections
+	 */
+	protected void assertAllResultingRejections(Set<Diff> resultingRejections) {
+		assertEquals(1, resultingRejections.size());
+		assertTrue(resultingRejections.contains(resultingRejection));
+	}
+
+	/**
+	 * Creates a new comparison.
+	 * 
+	 * @return new comparison instance
+	 */
+	private static Comparison createComparison() {
+		return CompareFactory.eINSTANCE.createComparison();
+	}
+
+	/**
+	 * Creates a new match.
+	 * 
+	 * @return new match instance
+	 */
+	private static Match createMatch() {
+		return CompareFactory.eINSTANCE.createMatch();
+	}
+
+	/**
+	 * Creates a new diff instance.
+	 * 
+	 * @param match
+	 * @return new diff instance
+	 */
+	private static Diff createDiff(Match match) {
+		Diff diff = new DiffSpec();
+		diff.setMatch(match);
+		return diff;
+	}
+
+	/**
+	 * Creates a new standalone instance of a merger registry.
+	 * 
+	 * @return new merger registry instance
+	 */
+	private static IMerger.Registry2 createMergerRegistry() {
+		return (Registry2)IMerger.RegistryImpl.createStandaloneInstance();
+	}
+
+	/**
+	 * Creates a mock merger that returns the given diffs as relationships for the testing diff.
+	 * 
+	 * @param diff
+	 *            testing diff
+	 * @param mergeDependency
+	 *            dependency of the testing diff
+	 * @param resultingMerge
+	 *            resulting diff for the testing diff
+	 * @param resultingRejection
+	 *            resulting rejection for the testing diff
+	 * @param ranking
+	 *            ranking of the mock merger
+	 * @param registry
+	 *            registry of the mock merger
+	 * @return a mock merger
+	 */
+	private static IMerger2 mockMerger(Diff diff, Diff mergeDependency, Diff resultingMerge,
+			Diff resultingRejection, int ranking, IMerger.Registry registry) {
+		IMerger2 mockMerger = mock(IMerger2.class);
+		when(mockMerger.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT))
+				.thenReturn(Sets.newHashSet(mergeDependency));
+		when(mockMerger.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT))
+				.thenReturn(Sets.newHashSet(resultingMerge));
+		when(mockMerger.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT))
+				.thenReturn(Sets.newHashSet(resultingRejection));
+		when(mockMerger.isMergerFor(any(Diff.class))).thenReturn(Boolean.TRUE);
+		when(mockMerger.getRanking()).thenReturn(ranking);
+		when(mockMerger.getRegistry()).thenReturn(registry);
+		return mockMerger;
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
index a69af81..21a233f 100644
--- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
@@ -11,6 +11,7 @@
  *     Stefan Dirix - Adds additional test classes
  *     Michael Borkowski - Adds additional test classes
  *     Martin Fleck - Add ImplicationMergeTest, GraphTest
+ *     Martin Fleck - Add DiffCacheAdapterTest
  *******************************************************************************/
 package org.eclipse.emf.compare.tests.suite;
 
@@ -22,6 +23,7 @@
 import org.eclipse.emf.compare.tests.conflict.PseudoConflictDetectionTest;
 import org.eclipse.emf.compare.tests.conflict.data.bug484557.Bug484557ConflictTest;
 import org.eclipse.emf.compare.tests.diff.ComparisonUtilTest;
+import org.eclipse.emf.compare.tests.diff.DiffRelationshipComputerTest;
 import org.eclipse.emf.compare.tests.diff.DiffUtilTest;
 import org.eclipse.emf.compare.tests.diff.FeatureFilterTest;
 import org.eclipse.emf.compare.tests.diff.FeatureMapMoveDiffTest;
@@ -108,7 +110,7 @@
 		RankedAdapterFactoryRegistryTest.class, ComparisonScopeAdapterTest.class,
 		EMFComparePredicatesTest.class, ImplicationsMergeTest.class, GraphTest.class,
 		ConflictImplicationsTest_Bug484579.class, PseudoConflictDetectionTest.class, ComplexMergeTest.class,
-		ConflictSearchTest.class, })
+		ConflictSearchTest.class, DiffRelationshipComputerTest.class })
 public class AllTests {
 	/**
 	 * Standalone launcher for all of compare's tests.
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java
deleted file mode 100644
index ed52856..0000000
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014, 2016 Obeo and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- * 
- * Contributors:
- *     Obeo - initial API and implementation
- *     Philip Langer - bug 462884
- *     Martin Fleck - bug 507177
- *******************************************************************************/
-package org.eclipse.emf.compare.internal.merge;
-
-import static com.google.common.base.Predicates.and;
-import static com.google.common.base.Predicates.not;
-import static org.eclipse.emf.compare.ConflictKind.PSEUDO;
-import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict;
-import static org.eclipse.emf.compare.utils.EMFComparePredicates.sameSideAs;
-
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.merge.IMerger;
-import org.eclipse.emf.compare.merge.IMerger2;
-
-/**
- * Factorizes utilities used throughout EMF Compare to explore merge dependencies.
- * 
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- */
-public final class MergeDependenciesUtil {
-	/** Hides default constructor. */
-	private MergeDependenciesUtil() {
-		// Hides default constructor
-	}
-
-	/**
-	 * Retrieves the set of all diffs related to the given <code>diff</code> when merging in the given
-	 * direction.
-	 * <p>
-	 * This is expected to return the set of all differences that will be need to merged along when a user
-	 * wishes to merge <code>diff</code>, either because they are required by it or because they are implied
-	 * by it one way or another.
-	 * </p>
-	 * <p>
-	 * Note that <code>diff</code> will be included in the returned set.
-	 * </p>
-	 * <p>
-	 * Also note that the resulting merges will contain the resulting rejections (diffs from the other side
-	 * that will be rejected)
-	 * </p>
-	 * 
-	 * @param diff
-	 *            The difference for which we seek all related ones.
-	 * @param mergerRegistry
-	 *            The {@link IMerger.Registry merger registry} currently in use.
-	 * @param rightToLeft
-	 *            The direction in which we're considering a merge.
-	 * @return The set of all diffs related to the given <code>diff</code> when merging in the given
-	 *         direction.
-	 */
-	public static Set<Diff> getAllResultingMerges(Diff diff, IMerger.Registry mergerRegistry,
-			boolean rightToLeft) {
-		final Set<Diff> resultingMerges = new LinkedHashSet<Diff>();
-		resultingMerges.add(diff);
-
-		Set<Diff> relations = internalGetResultingMerges(diff, mergerRegistry, rightToLeft);
-		// We don't want to take in account pseudo conflicts since there is nothing to do with them
-		// and their dependencies may cause incorrect merge dependencies computation.
-		Set<Diff> difference = Sets.filter(Sets.difference(relations, resultingMerges),
-				not(hasConflict(PSEUDO)));
-		while (!difference.isEmpty()) {
-			final Set<Diff> newRelations = new LinkedHashSet<Diff>(difference);
-			resultingMerges.addAll(newRelations);
-			relations = new LinkedHashSet<Diff>();
-			for (Diff newRelation : newRelations) {
-				Set<Diff> internalResultingMerges = internalGetResultingMerges(newRelation, mergerRegistry,
-						rightToLeft);
-				// We don't want to take in account pseudo conflicts since there is nothing to do with them
-				// and there dependencies may cause incorrect merge dependencies computation.
-				relations.addAll(Sets.filter(internalResultingMerges, not(hasConflict(PSEUDO))));
-			}
-			difference = Sets.difference(relations, resultingMerges);
-		}
-
-		// If a pseudo conflict is directly selected, we want to display other diffs of the pseudo conflict as
-		// resulting merge for the user
-		if (diff.getConflict() != null && diff.getConflict().getKind() == PSEUDO) {
-			resultingMerges.addAll(diff.getConflict().getDifferences());
-		}
-
-		return resultingMerges;
-	}
-
-	/**
-	 * Returns the set of all differences <b>directly</b> related to the given one, either as dependencies or
-	 * as implications.
-	 * 
-	 * @param diff
-	 *            The difference for which we seek all directly related others.
-	 * @param mergerRegistry
-	 *            The {@link IMerger.Registry merger registry} currently in use.
-	 * @param rightToLeft
-	 *            The direction in which we're considering a merge.
-	 * @return The set of all differences <b>directly</b> related to the given one.
-	 */
-	private static Set<Diff> internalGetResultingMerges(Diff diff, IMerger.Registry mergerRegistry,
-			boolean rightToLeft) {
-		final IMerger merger = mergerRegistry.getHighestRankingMerger(diff);
-		final Set<Diff> directParents;
-		final Set<Diff> directImplications;
-		if (merger instanceof IMerger2) {
-			directParents = ((IMerger2)merger).getDirectMergeDependencies(diff, rightToLeft);
-			directImplications = ((IMerger2)merger).getDirectResultingMerges(diff, rightToLeft);
-		} else {
-			directParents = Collections.emptySet();
-			directImplications = Collections.emptySet();
-		}
-
-		final SetView<Diff> directRelated = Sets.union(directParents, directImplications);
-
-		return directRelated;
-	}
-
-	/**
-	 * Retrieves the set of all diffs that will be rejected if the given <code>diff</code> is merged, either
-	 * because of unresolveable conflicts or because of unreachable requirements.
-	 * 
-	 * @param diff
-	 *            The difference for which we seek all opposite ones.
-	 * @param mergerRegistry
-	 *            The {@link IMerger.Registry merger registry} currently in use.
-	 * @param mergeRightToLeft
-	 *            The direction in which we're considering a merge.
-	 * @return The set of all diffs that will be rejected if the given <code>diff</code> is merged in the
-	 *         given direction.
-	 */
-	public static Set<Diff> getAllResultingRejections(Diff diff, IMerger.Registry mergerRegistry,
-			boolean mergeRightToLeft) {
-		final Set<Diff> resultingRejections = new LinkedHashSet<Diff>();
-
-		final Set<Diff> allResultingMerges = getAllResultingMerges(diff, mergerRegistry, mergeRightToLeft);
-		resultingRejections.addAll(
-				Sets.filter(allResultingMerges, and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
-		// Only search rejections caused by diffs on the same side
-		for (Diff resulting : Sets.filter(allResultingMerges, sameSideAs(diff))) {
-			Set<Diff> rejections = internalGetResultingRejections(resulting, mergerRegistry,
-					mergeRightToLeft);
-			// We don't want to take in account pseudo conflicts since there is nothing to do with them
-			// and their dependencies may cause incorrect merge dependencies computation.
-			Set<Diff> difference = Sets.filter(rejections, not(hasConflict(PSEUDO)));
-			while (!difference.isEmpty()) {
-				final Set<Diff> newRejections = new LinkedHashSet<Diff>(difference);
-				resultingRejections.addAll(newRejections);
-				rejections = new LinkedHashSet<Diff>();
-				for (Diff rejected : newRejections) {
-					final IMerger merger = mergerRegistry.getHighestRankingMerger(rejected);
-					if (merger instanceof IMerger2) {
-						Set<Diff> directMergeDependencies = ((IMerger2)merger)
-								.getDirectMergeDependencies(rejected, mergeRightToLeft);
-						// We don't want to take in account pseudo conflicts since there is nothing to do with
-						// them and their dependencies may cause incorrect merge dependencies computation.
-						// We also don't want to consider diffs on the same side for rejections
-						rejections.addAll(Sets.filter(directMergeDependencies,
-								and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
-						Set<Diff> directResultingMerges = ((IMerger2)merger)
-								.getDirectResultingMerges(rejected, mergeRightToLeft);
-						rejections.addAll(Sets.filter(directResultingMerges,
-								and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
-					}
-				}
-				difference = Sets.difference(rejections, resultingRejections);
-			}
-		}
-		return resultingRejections;
-	}
-
-	/**
-	 * Returns the set of differences directly related to <code>diff</code> that will be rejected if it is
-	 * merged.
-	 * 
-	 * @param diff
-	 *            The difference for which we seek all opposite ones.
-	 * @param mergerRegistry
-	 *            The {@link IMerger.Registry merger registry} currently in use.
-	 * @param rightToLeft
-	 *            The direction in which we're considering a merge.
-	 * @return The set of all directly related differences that will be rejected if <code>diff</code> is
-	 *         merged in the given direction.
-	 */
-	private static Set<Diff> internalGetResultingRejections(Diff diff, IMerger.Registry mergerRegistry,
-			boolean rightToLeft) {
-		final IMerger merger = mergerRegistry.getHighestRankingMerger(diff);
-		if (merger instanceof IMerger2) {
-			return ((IMerger2)merger).getDirectResultingRejections(diff, rightToLeft);
-		}
-		return Collections.emptySet();
-	}
-}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java
index b49795e..f129cc3 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java
@@ -40,6 +40,9 @@
 	/** Filter the differences that should be merged. */
 	private final Predicate<? super Diff> filter;
 
+	/** The relationship computer used to calculate dependencies and requirements of diffs. */
+	private IDiffRelationshipComputer relationshipComputer;
+
 	/**
 	 * Constructs our batch merger provided the registry from which to retrieve the delegate mergers. Using
 	 * such a merger will merge every differences passed to its "copy" methods : conflictual or not.
@@ -49,7 +52,19 @@
 	 *            IMerger.Registry2.
 	 */
 	public BatchMerger(IMerger.Registry registry) {
-		this(registry, alwaysTrue());
+		this(new DiffRelationshipComputer(registry), alwaysTrue());
+	}
+
+	/**
+	 * Constructs our batch merger provided the registry from which to retrieve the delegate mergers. Using
+	 * such a merger will merge every differences passed to its "copy" methods : conflictual or not.
+	 * 
+	 * @param relationshipComputer
+	 *            The relationship computer used to calculate dependencies and requirements of diffs.
+	 * @since 3.5
+	 */
+	public BatchMerger(IDiffRelationshipComputer relationshipComputer) {
+		this(relationshipComputer, alwaysTrue());
 	}
 
 	/**
@@ -79,7 +94,38 @@
 	 * @see org.eclipse.emf.compare.utils.EMFComparePredicates
 	 */
 	public BatchMerger(IMerger.Registry registry, Predicate<? super Diff> filter) {
-		this.registry = (IMerger.Registry2)checkNotNull(registry);
+		this(new DiffRelationshipComputer(registry), filter);
+	}
+
+	/**
+	 * Constructs our batch merger provided the registry from which to retrieve the delegate mergers, and a
+	 * filter if you only wish to merge specific differences.
+	 * <p>
+	 * <b>Note</b> that the filter indicates differences that will be merged, not those that will be ignored.
+	 * </p>
+	 * <p>
+	 * For example, if you wish to ignore all differences in conflict, you can use :
+	 * 
+	 * <pre>
+	 * IMerger.Registry registry = IMerger.RegistryImpl.createStandaloneInstance();
+	 * IBatchMerger bathMerger = new BatchMerger(registry, {@link com.google.common.base.Predicates#not(Predicate) not}({@link org.eclipse.emf.compare.utils.EMFComparePredicates#hasConflict(org.eclipse.emf.compare.ConflictKind...) hasConflict}(ConflictKind.PSEUDO, ConflictKind.REAL)));
+	 * bathMerger.copyAll...
+	 * </pre>
+	 * </p>
+	 * 
+	 * @param filter
+	 *            Additional filter for the differences. This could be set in order to ignore diffs
+	 *            originating from a given side. Note that the filter describes the differences that will be
+	 *            merged, not those that will be ignored.
+	 * @param relationshipComputer
+	 *            The relationship computer used to calculate dependencies and requirements of diffs.
+	 * @see com.google.common.base.Predicates
+	 * @see org.eclipse.emf.compare.utils.EMFComparePredicates
+	 * @since 3.5
+	 */
+	public BatchMerger(IDiffRelationshipComputer relationshipComputer, Predicate<? super Diff> filter) {
+		this.relationshipComputer = checkNotNull(relationshipComputer);
+		this.registry = (IMerger.Registry2)checkNotNull(relationshipComputer.getMergerRegistry());
 		this.filter = checkNotNull(filter);
 	}
 
@@ -95,7 +141,7 @@
 			start = System.currentTimeMillis();
 			LOGGER.debug("copyAllLeftToRight(differences, monitor) - Start"); //$NON-NLS-1$
 		}
-		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(false, registry);
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(false, relationshipComputer);
 		for (Diff diff : Iterables.filter(differences, filter)) {
 			if (!AbstractMerger.isInTerminalState(diff)) {
 				Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff);
@@ -129,7 +175,7 @@
 			start = System.currentTimeMillis();
 			LOGGER.debug("copyAllRightToLeft(differences, monitor) - Start"); //$NON-NLS-1$
 		}
-		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, registry);
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer);
 		for (Diff diff : Iterables.filter(differences, filter)) {
 			if (!AbstractMerger.isInTerminalState(diff)) {
 				Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff);
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java
new file mode 100644
index 0000000..64bdc77
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java
@@ -0,0 +1,524 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.merge;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.merge.IMerger.Registry;
+
+/**
+ * A computer implementation to cache the relationship of diffs.
+ * 
+ * @since 3.5
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ * @see IMerger2
+ */
+public class CachingDiffRelationshipComputer implements IDiffRelationshipComputer {
+
+	/** A computer instance to calculate the relationships between diffs. */
+	protected IDiffRelationshipComputer computer;
+
+	/** Direct merge dependencies: right to left. */
+	protected Map<Diff, Set<Diff>> directMergeDependenciesR2L = new ConcurrentHashMap<>();
+
+	/** Direct merge dependencies: left to right. */
+	protected Map<Diff, Set<Diff>> directMergeDependenciesL2R = new ConcurrentHashMap<>();
+
+	/** Direct resulting merges: right to left. */
+	protected Map<Diff, Set<Diff>> directResultingMergesR2L = new ConcurrentHashMap<>();
+
+	/** Direct resulting merges: left to right. */
+	protected Map<Diff, Set<Diff>> directResultingMergesL2R = new ConcurrentHashMap<>();
+
+	/** Direct resulting rejections: right to left. */
+	protected Map<Diff, Set<Diff>> directResultingRejectionsR2L = new ConcurrentHashMap<>();
+
+	/** Direct resulting rejections: left to right. */
+	protected Map<Diff, Set<Diff>> directResultingRejectionsL2R = new ConcurrentHashMap<>();
+
+	/** All resulting merges: right to left. */
+	protected Map<Diff, Set<Diff>> allResultingMergesR2L = new ConcurrentHashMap<>();
+
+	/** All resulting merges: left to right. */
+	protected Map<Diff, Set<Diff>> allResultingMergesL2R = new ConcurrentHashMap<>();
+
+	/** All resulting rejections: right to left. */
+	protected Map<Diff, Set<Diff>> allResultingRejectionsR2L = new ConcurrentHashMap<>();
+
+	/** All resulting rejections: left to right. */
+	protected Map<Diff, Set<Diff>> allResultingRejectionsL2R = new ConcurrentHashMap<>();
+
+	/**
+	 * Creates a new computer with the given registry.
+	 * 
+	 * @param registry
+	 *            merger registry
+	 */
+	public CachingDiffRelationshipComputer(IMerger.Registry registry) {
+		this(registry, IMergeCriterion.NONE);
+	}
+
+	/**
+	 * Creates a new computer with the given registry and merge criterion.
+	 * 
+	 * @param registry
+	 *            merger registry
+	 * @param criterion
+	 *            merge criterion used to get the merger from the registry, use {@link IMergeCriterion#NONE}
+	 *            if no special criterion should be set.
+	 */
+	public CachingDiffRelationshipComputer(IMerger.Registry registry, IMergeCriterion criterion) {
+		this(new DiffRelationshipComputer(registry, criterion));
+	}
+
+	/**
+	 * Creates a new computer by wrapping the given instance. Any computing calls are delegated to this
+	 * instance and cached.
+	 * 
+	 * @param computer
+	 *            computer instance used for calculating diff relationships.
+	 */
+	public CachingDiffRelationshipComputer(IDiffRelationshipComputer computer) {
+		this.computer = computer;
+	}
+
+	/**
+	 * Returns the internal computer instance used to compute the diff relationships.
+	 * 
+	 * @return internal computer instance.
+	 */
+	protected IDiffRelationshipComputer getComputer() {
+		return computer;
+	}
+
+	@Override
+	public Registry getMergerRegistry() {
+		return getComputer().getMergerRegistry();
+	}
+
+	/**
+	 * {@inheritDoc} WARNING: Setting the merger registry invalidates previously cached results, if another
+	 * registry was set previously!
+	 */
+	public void setMergerRegistry(Registry mergerRegistry) {
+		if (getMergerRegistry() != mergerRegistry) {
+			getComputer().setMergerRegistry(mergerRegistry);
+			invalidate();
+		}
+	}
+
+	@Override
+	public IMergeCriterion getMergeCriterion() {
+		return getComputer().getMergeCriterion();
+	}
+
+	@Override
+	public IMerger2 getMerger(Diff diff) {
+		return getComputer().getMerger(diff);
+	}
+
+	@Override
+	public boolean hasMerger(Diff diff) {
+		return getComputer().hasMerger(diff);
+	}
+
+	/**
+	 * {@inheritDoc} WARNING: Setting the merge criterion invalidates previously cached results, if another
+	 * criterion was set previously.
+	 */
+	public void setMergeCriterion(IMergeCriterion mergeCriterion) {
+		if (getMergeCriterion() != mergeCriterion) {
+			getComputer().setMergeCriterion(mergeCriterion);
+			invalidate();
+		}
+	}
+
+	/**
+	 * Caches the given direct merge dependencies.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @param directMergeDependencies
+	 *            direct merge dependencies of diff
+	 */
+	protected void setCachedDirectMergeDependencies(Diff diff, boolean mergeRightToLeft,
+			Set<Diff> directMergeDependencies) {
+		if (mergeRightToLeft) {
+			directMergeDependenciesR2L.put(diff, directMergeDependencies);
+		} else {
+			directMergeDependenciesL2R.put(diff, directMergeDependencies);
+		}
+	}
+
+	/**
+	 * Returns the cached direct merge dependencies.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct merge dependencies
+	 */
+	protected Set<Diff> getCachedDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
+		if (mergeRightToLeft) {
+			return directMergeDependenciesR2L.get(diff);
+		} else {
+			return directMergeDependenciesL2R.get(diff);
+		}
+	}
+
+	/**
+	 * Computes direct merge dependencies for the given diff.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return a non-null set of direct merge dependencies
+	 */
+	protected Set<Diff> computeDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
+		return getComputer().getDirectMergeDependencies(diff, mergeRightToLeft);
+	}
+
+	/**
+	 * Returns the cached direct merge dependencies, if present. Otherwise, the direct merge dependencies are
+	 * retrieved and cached using the given merger.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct merge dependencies
+	 * @see IMerger2#getDirectMergeDependencies(Diff, boolean)
+	 */
+	@Override
+	public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
+		Set<Diff> directMergeDependencies = getCachedDirectMergeDependencies(diff, mergeRightToLeft);
+		if (directMergeDependencies == null) {
+			directMergeDependencies = computeDirectMergeDependencies(diff, mergeRightToLeft);
+			setCachedDirectMergeDependencies(diff, mergeRightToLeft, directMergeDependencies);
+		}
+		return directMergeDependencies;
+	}
+
+	/**
+	 * Caches the given direct resulting merges.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @param directResultingMerges
+	 *            direct resulting merges
+	 */
+	protected void setCachedDirectResultingMerges(Diff diff, boolean mergeRightToLeft,
+			Set<Diff> directResultingMerges) {
+		if (mergeRightToLeft) {
+			directResultingMergesR2L.put(diff, directResultingMerges);
+		} else {
+			directResultingMergesL2R.put(diff, directResultingMerges);
+		}
+	}
+
+	/**
+	 * Returns the cached direct resulting merges.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct resulting merges
+	 */
+	protected Set<Diff> getCachedDirectResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		if (mergeRightToLeft) {
+			return directResultingMergesR2L.get(diff);
+		} else {
+			return directResultingMergesL2R.get(diff);
+		}
+	}
+
+	/**
+	 * Computes direct resulting merges for the given diff.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return a non-null set of all resulting merges
+	 */
+	protected Set<Diff> computeDirectResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		return getComputer().getDirectResultingMerges(diff, mergeRightToLeft);
+	}
+
+	/**
+	 * Returns the cached direct resulting merges, if present. Otherwise, the direct resulting merges are
+	 * retrieved and cached using the given merger.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct resulting merges
+	 * @see IMerger2#getDirectResultingMerges(Diff, boolean)
+	 */
+	@Override
+	public Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		Set<Diff> directResultingMerges = getCachedDirectResultingMerges(diff, mergeRightToLeft);
+		if (directResultingMerges == null) {
+			directResultingMerges = computeDirectResultingMerges(diff, mergeRightToLeft);
+			setCachedDirectResultingMerges(diff, mergeRightToLeft, directResultingMerges);
+		}
+		return directResultingMerges;
+	}
+
+	/**
+	 * Caches the given direct resulting rejections.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @param directResultingRejections
+	 *            direct resulting rejections
+	 */
+	protected void setCachedDirectResultingRejections(Diff diff, boolean mergeRightToLeft,
+			Set<Diff> directResultingRejections) {
+		if (mergeRightToLeft) {
+			directResultingRejectionsR2L.put(diff, directResultingRejections);
+		} else {
+			directResultingRejectionsL2R.put(diff, directResultingRejections);
+		}
+	}
+
+	/**
+	 * Returns the cached direct resulting rejections.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct resulting rejections
+	 */
+	protected Set<Diff> getCachedDirectResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		if (mergeRightToLeft) {
+			return directResultingRejectionsR2L.get(diff);
+		} else {
+			return directResultingRejectionsL2R.get(diff);
+		}
+	}
+
+	/**
+	 * Computes the direct resulting rejections.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return a non-null set of direct resulting rejections
+	 */
+	protected Set<Diff> computeDirectResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		return getComputer().getDirectResultingRejections(diff, mergeRightToLeft);
+	}
+
+	@Override
+	public Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		Set<Diff> directResultingRejections = getCachedDirectResultingRejections(diff, mergeRightToLeft);
+		if (directResultingRejections == null) {
+			directResultingRejections = computeDirectResultingRejections(diff, mergeRightToLeft);
+			setCachedDirectResultingRejections(diff, mergeRightToLeft, directResultingRejections);
+		}
+		return directResultingRejections;
+	}
+
+	/**
+	 * Caches the given all resulting rejections.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @param allResultingRejections
+	 *            all resulting rejections
+	 */
+	protected void setCachedAllResultingRejections(Diff diff, boolean mergeRightToLeft,
+			Set<Diff> allResultingRejections) {
+		if (mergeRightToLeft) {
+			allResultingRejectionsR2L.put(diff, allResultingRejections);
+		} else {
+			allResultingRejectionsL2R.put(diff, allResultingRejections);
+		}
+	}
+
+	/**
+	 * Returns the cached all resulting rejections.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached all resulting rejections
+	 */
+	protected Set<Diff> getCachedAllResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		if (mergeRightToLeft) {
+			return allResultingRejectionsR2L.get(diff);
+		} else {
+			return allResultingRejectionsL2R.get(diff);
+		}
+	}
+
+	/**
+	 * Computes all resulting rejections for the given diff.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return a non-null set of all resulting rejections
+	 */
+	protected Set<Diff> computeAllResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		return getComputer().getAllResultingRejections(diff, mergeRightToLeft);
+	}
+
+	@Override
+	public Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		Set<Diff> allResultingRejections = getCachedAllResultingRejections(diff, mergeRightToLeft);
+		if (allResultingRejections == null) {
+			allResultingRejections = computeAllResultingRejections(diff, mergeRightToLeft);
+			setCachedAllResultingRejections(diff, mergeRightToLeft, allResultingRejections);
+		}
+		return allResultingRejections;
+	}
+
+	/**
+	 * Caches the given all resulting merges.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @param allResultingMerges
+	 *            all resulting merges
+	 */
+	protected void setCachedAllResultingMerges(Diff diff, boolean mergeRightToLeft,
+			Set<Diff> allResultingMerges) {
+		if (mergeRightToLeft) {
+			allResultingMergesR2L.put(diff, allResultingMerges);
+		} else {
+			allResultingMergesL2R.put(diff, allResultingMerges);
+		}
+	}
+
+	/**
+	 * Returns the cached all resulting merges.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached all resulting merges
+	 */
+	protected Set<Diff> getCachedAllResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		if (mergeRightToLeft) {
+			return allResultingMergesR2L.get(diff);
+		} else {
+			return allResultingMergesL2R.get(diff);
+		}
+	}
+
+	/**
+	 * Computes all resulting merges for the given diff.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return a non-null set of all resulting merges
+	 */
+	protected Set<Diff> computeAllResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		return getComputer().getAllResultingMerges(diff, mergeRightToLeft);
+	}
+
+	/**
+	 * Returns the cached direct resulting merges, if present. Otherwise, the direct resulting merges are
+	 * retrieved and cached using the given merger.
+	 * 
+	 * @param diff
+	 *            diff
+	 * @param mergeRightToLeft
+	 *            merge direction
+	 * @return cached direct resulting merges
+	 * @see IMerger2#getDirectResultingMerges(Diff, boolean)
+	 */
+	@Override
+	public Set<Diff> getAllResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		Set<Diff> allResultingMerges = getCachedAllResultingMerges(diff, mergeRightToLeft);
+		if (allResultingMerges == null) {
+			allResultingMerges = computeAllResultingMerges(diff, mergeRightToLeft);
+			setCachedAllResultingMerges(diff, mergeRightToLeft, allResultingMerges);
+		}
+		return allResultingMerges;
+	}
+
+	/**
+	 * Invalidates the cache for the given diff, so that relationships will be re-calculated for this diff the
+	 * next time a respective method is called.
+	 * 
+	 * @param diff
+	 *            diff for which the cache should be invalidated.
+	 */
+	public void invalidate(Diff diff) {
+		directMergeDependenciesR2L.remove(diff);
+		directMergeDependenciesL2R.remove(diff);
+		directResultingRejectionsR2L.remove(diff);
+		directResultingRejectionsL2R.remove(diff);
+		directResultingMergesR2L.remove(diff);
+		directResultingMergesL2R.remove(diff);
+		allResultingMergesL2R.remove(diff);
+		allResultingMergesR2L.remove(diff);
+		allResultingRejectionsL2R.remove(diff);
+		allResultingRejectionsR2L.remove(diff);
+	}
+
+	/**
+	 * Invalidates the cache for all given diffs, so that relationships will be re-calculated for each diff
+	 * the next time a respective method is called.
+	 * 
+	 * @param diffs
+	 *            diffs for which the cache should be invalidated.
+	 */
+	public void invalidate(Iterable<Diff> diffs) {
+		for (Diff diff : diffs) {
+			invalidate(diff);
+		}
+	}
+
+	/**
+	 * Invalidates the complete cache, so that relationships will be re-calculated any diff the next time a
+	 * respective method is called.
+	 */
+	public void invalidate() {
+		directMergeDependenciesR2L.clear();
+		directMergeDependenciesL2R.clear();
+		directResultingRejectionsR2L.clear();
+		directResultingRejectionsL2R.clear();
+		directResultingMergesR2L.clear();
+		directResultingMergesL2R.clear();
+		allResultingMergesL2R.clear();
+		allResultingMergesR2L.clear();
+		allResultingRejectionsL2R.clear();
+		allResultingRejectionsR2L.clear();
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java
index 02b5692..c1469d5 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 Obeo.
+ * Copyright (c) 2017 Obeo and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,10 +7,10 @@
  * 
  * Contributors:
  *     Obeo - initial API and implementation
+ *     Martin Fleck - bug 514415
  *******************************************************************************/
 package org.eclipse.emf.compare.merge;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.eclipse.emf.compare.ConflictKind.REAL;
 import static org.eclipse.emf.compare.merge.IMergeCriterion.NONE;
 
@@ -54,16 +54,6 @@
 	private final boolean rightToLeft;
 
 	/**
-	 * The merge criterion to use, can be <code>null</code>.
-	 */
-	private IMergeCriterion criterion;
-
-	/**
-	 * The merger registry to use.
-	 */
-	private IMerger.Registry2 registry;
-
-	/**
 	 * The ordered set of diffs to merge.
 	 */
 	private Set<Diff> result = new LinkedHashSet<Diff>();
@@ -77,6 +67,9 @@
 	 */
 	private Predicate<? super Conflict> conflictChecker;
 
+	/** The relationship computer used to calculate dependencies and requirements of diffs. */
+	private IDiffRelationshipComputer relationshipComputer;
+
 	/**
 	 * Constructor.
 	 * 
@@ -86,7 +79,7 @@
 	 *            The Registry to use.
 	 */
 	public ComputeDiffsToMerge(boolean rightToLeft, IMerger.Registry2 registry) {
-		this(rightToLeft, registry, NONE);
+		this(rightToLeft, new DiffRelationshipComputer(registry, NONE));
 	}
 
 	/**
@@ -100,9 +93,20 @@
 	 *            The merge criterion, must not be <code>null</code>
 	 */
 	public ComputeDiffsToMerge(boolean rightToLeft, IMerger.Registry2 registry, IMergeCriterion criterion) {
+		this(rightToLeft, new DiffRelationshipComputer(registry, criterion));
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param rightToLeft
+	 *            The merge direction
+	 * @param relationshipComputer
+	 *            The relationship computer used to calculate dependencies and requirements of diffs.
+	 */
+	public ComputeDiffsToMerge(boolean rightToLeft, IDiffRelationshipComputer relationshipComputer) {
 		this.rightToLeft = rightToLeft;
-		this.registry = registry;
-		this.criterion = checkNotNull(criterion);
+		this.relationshipComputer = relationshipComputer;
 	}
 
 	/**
@@ -179,16 +183,17 @@
 				diffsThatLedToConflict.add(diff);
 				throw new MergeBlockedByConflictException(diffsThatLedToConflict);
 			}
-			DelegatingMerger mergerDelegate = AbstractMerger.getMergerDelegate(diff, registry, criterion);
-			IMerger merger = mergerDelegate.getMerger();
-			if (merger instanceof IMerger2) {
-				Set<Diff> dependencies = ((IMerger2)merger).getDirectMergeDependencies(diff, rightToLeft);
+
+			if (relationshipComputer.hasMerger(diff)) {
+				Set<Diff> dependencies = relationshipComputer.getDirectMergeDependencies(diff, rightToLeft);
 				for (Diff required : dependencies) {
 					addDiff(required, consequences);
 				}
+
 				result.add(diff);
 				computing.remove(diff);
-				consequences.addAll(((IMerger2)merger).getDirectResultingMerges(diff, rightToLeft));
+
+				consequences.addAll(relationshipComputer.getDirectResultingMerges(diff, rightToLeft));
 			} else {
 				result.add(diff);
 			}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java
new file mode 100644
index 0000000..c144f3b
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.merge;
+
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.not;
+import static org.eclipse.emf.compare.ConflictKind.PSEUDO;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.sameSideAs;
+
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.merge.IMerger.Registry;
+import org.eclipse.emf.compare.merge.IMerger.Registry2;
+
+/**
+ * A computer implementation to calculate the relationship of differences in EMF Compare.
+ * 
+ * @since 3.5
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ * @see IMerger2
+ */
+public class DiffRelationshipComputer implements IDiffRelationshipComputer {
+
+	/** Merger registry used to retrieve the correct merger. */
+	protected Registry registry;
+
+	/** Merge criterion used to retrieve the correct merger. */
+	protected IMergeCriterion criterion;
+
+	/**
+	 * Creates a new relationship computer.
+	 * 
+	 * @param registry
+	 *            merger registry
+	 */
+	public DiffRelationshipComputer(IMerger.Registry registry) {
+		this(registry, IMergeCriterion.NONE);
+	}
+
+	/**
+	 * Creates a new relationship computer.
+	 * 
+	 * @param registry
+	 *            merger registry
+	 * @param criterion
+	 *            merge criterion used to get the merger from the registry, use {@link IMergeCriterion#NONE}
+	 *            if no special criterion should be set.
+	 */
+	public DiffRelationshipComputer(IMerger.Registry registry, IMergeCriterion criterion) {
+		this.registry = registry;
+		this.criterion = criterion;
+	}
+
+	@Override
+	public Registry getMergerRegistry() {
+		return registry;
+	}
+
+	@Override
+	public void setMergerRegistry(Registry mergerRegistry) {
+		this.registry = mergerRegistry;
+	}
+
+	@Override
+	public IMergeCriterion getMergeCriterion() {
+		return criterion;
+	}
+
+	@Override
+	public void setMergeCriterion(IMergeCriterion mergeCriterion) {
+		this.criterion = mergeCriterion;
+	}
+
+	/**
+	 * Returns the {@link #getMergerRegistry() merger registry} as {@link Registry2}, if possible. If the
+	 * merger registry is not an instance of {@link Registry2}, null is returned.
+	 * 
+	 * @return {@link Registry2} instance or null.
+	 */
+	protected Registry2 getMergerRegistry2() {
+		if (registry instanceof Registry2) {
+			return (Registry2)registry;
+		}
+		return null;
+	}
+
+	@Override
+	public IMerger2 getMerger(Diff diff) {
+		if (getMergerRegistry2() == null) {
+			return null;
+		}
+
+		DelegatingMerger mergerDelegate = AbstractMerger.getMergerDelegate(diff, getMergerRegistry2(),
+				getMergeCriterion());
+		IMerger merger = mergerDelegate.getMerger();
+		if (!(merger instanceof IMerger2)) {
+			return null;
+		}
+
+		return (IMerger2)merger;
+	}
+
+	@Override
+	public boolean hasMerger(Diff diff) {
+		return getMerger(diff) != null;
+	}
+
+	@Override
+	public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
+		IMerger2 merger = getMerger(diff);
+		if (merger != null) {
+			return merger.getDirectMergeDependencies(diff, mergeRightToLeft);
+		}
+		return Sets.newHashSet();
+	}
+
+	@Override
+	public Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft) {
+		IMerger2 merger = getMerger(diff);
+		if (merger != null) {
+			return merger.getDirectResultingMerges(diff, mergeRightToLeft);
+		}
+		return Sets.newHashSet();
+	}
+
+	@Override
+	public Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		IMerger2 merger = getMerger(diff);
+		if (merger != null) {
+			return merger.getDirectResultingRejections(diff, mergeRightToLeft);
+		} else {
+			return Sets.newHashSet();
+		}
+	}
+
+	@Override
+	public Set<Diff> getAllResultingMerges(Diff diff, boolean rightToLeft) {
+		final Set<Diff> resultingMerges = new LinkedHashSet<Diff>();
+		resultingMerges.add(diff);
+
+		Set<Diff> relations = internalGetAllResultingMerges(diff, rightToLeft);
+		// We don't want to take in account pseudo conflicts since there is nothing to do with them
+		// and their dependencies may cause incorrect merge dependencies computation.
+		Set<Diff> difference = Sets.filter(Sets.difference(relations, resultingMerges),
+				not(hasConflict(PSEUDO)));
+		while (!difference.isEmpty()) {
+			final Set<Diff> newRelations = new LinkedHashSet<Diff>(difference);
+			resultingMerges.addAll(newRelations);
+			relations = new LinkedHashSet<Diff>();
+			for (Diff newRelation : newRelations) {
+				Set<Diff> internalResultingMerges = internalGetAllResultingMerges(newRelation, rightToLeft);
+				// We don't want to take in account pseudo conflicts since there is nothing to do with them
+				// and there dependencies may cause incorrect merge dependencies computation.
+				relations.addAll(Sets.filter(internalResultingMerges, not(hasConflict(PSEUDO))));
+			}
+			difference = Sets.difference(relations, resultingMerges);
+		}
+
+		// If a pseudo conflict is directly selected, we want to display other diffs of the pseudo conflict as
+		// resulting merge for the user
+		if (diff.getConflict() != null && diff.getConflict().getKind() == PSEUDO) {
+			resultingMerges.addAll(diff.getConflict().getDifferences());
+		}
+
+		return resultingMerges;
+	}
+
+	/**
+	 * Returns the set of all differences <b>directly</b> related to the given one, either as dependencies or
+	 * as implications.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek all directly related others.
+	 * @param rightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return The set of all differences <b>directly</b> related to the given one.
+	 */
+	protected Set<Diff> internalGetAllResultingMerges(Diff diff, boolean rightToLeft) {
+		final Set<Diff> directParents = getDirectMergeDependencies(diff, rightToLeft);
+		final Set<Diff> directImplications = getDirectResultingMerges(diff, rightToLeft);
+		final SetView<Diff> directRelated = Sets.union(directParents, directImplications);
+		return directRelated;
+	}
+
+	@Override
+	public Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft) {
+		final Set<Diff> resultingRejections = new LinkedHashSet<Diff>();
+		final Set<Diff> allResultingMerges = getAllResultingMerges(diff, mergeRightToLeft);
+		resultingRejections.addAll(
+				Sets.filter(allResultingMerges, and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
+		// Only search rejections caused by diffs on the same side
+		for (Diff resulting : Sets.filter(allResultingMerges, sameSideAs(diff))) {
+			Set<Diff> rejections = getDirectResultingRejections(resulting, mergeRightToLeft);
+			// We don't want to take in account pseudo conflicts since there is nothing to do with them
+			// and their dependencies may cause incorrect merge dependencies computation.
+			Set<Diff> difference = Sets.filter(rejections, not(hasConflict(PSEUDO)));
+			while (!difference.isEmpty()) {
+				final Set<Diff> newRejections = new LinkedHashSet<Diff>(difference);
+				resultingRejections.addAll(newRejections);
+				rejections = new LinkedHashSet<Diff>();
+				for (Diff rejected : newRejections) {
+					Set<Diff> directMergeDependencies = getDirectMergeDependencies(rejected,
+							mergeRightToLeft);
+					// We don't want to take in account pseudo conflicts since there is nothing to do with
+					// them and their dependencies may cause incorrect merge dependencies computation.
+					// We also don't want to consider diffs on the same side for rejections
+					rejections.addAll(Sets.filter(directMergeDependencies,
+							and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
+					Set<Diff> directResultingMerges = getDirectResultingMerges(rejected, mergeRightToLeft);
+					rejections.addAll(Sets.filter(directResultingMerges,
+							and(not(hasConflict(PSEUDO)), not(sameSideAs(diff)))));
+				}
+				difference = Sets.difference(rejections, resultingRejections);
+			}
+		}
+		return resultingRejections;
+	}
+
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java
new file mode 100644
index 0000000..56ceaab
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource Services GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *      Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.merge;
+
+import java.util.Set;
+
+import org.eclipse.emf.compare.Diff;
+
+/**
+ * A computer to retrieve the dependent diffs, resulting merges and resulting rejections for a diff.
+ * 
+ * @since 3.5
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ * @see IMerger2
+ */
+public interface IDiffRelationshipComputer {
+	/**
+	 * Retrieves the set of directly required diffs needed in order to merge the given <code>diff</code>. This
+	 * may includes the diff's requirements or any other diff that we need to merge before the given one.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek the direct merge dependencies.
+	 * @param mergeRightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return A non-null set of direct merge dependencies.
+	 * @see IMerger2#getDirectMergeDependencies(Diff, boolean)
+	 */
+	Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft);
+
+	/**
+	 * Returns all differences that will be merged because of our merging the given <code>diff</code>. This
+	 * may include the diff's implications, the diff's equivalences, the diff's refinements or any other diff
+	 * that we need to merge together with the given diff.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek the direct merge dependencies.
+	 * @param mergeRightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return A non-null set of direct resulting merges.
+	 * @see IMerger2#getDirectResultingMerges(Diff, boolean)
+	 */
+	Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft);
+
+	/**
+	 * Returns the set of all differences that need to be rejected if <code>diff</code> is merged in the given
+	 * direction.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek the direct resulting rejections.
+	 * @param mergeRightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return A non-null set of direct resulting rejections.
+	 * @see #getAllResultingRejections(Diff, boolean)
+	 * @see IMerger2#getDirectResultingRejections(Diff, boolean)
+	 */
+	Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft);
+
+	/**
+	 * Retrieves the set of all diffs related to the given <code>diff</code> when merging in the given
+	 * direction.
+	 * <p>
+	 * This is expected to return the set of all differences that will be need to merged along when a user
+	 * wishes to merge <code>diff</code>, either because they are required by it or because they are implied
+	 * by it one way or another.
+	 * </p>
+	 * <p>
+	 * Note that <code>diff</code> will be included in the returned set.
+	 * </p>
+	 * <p>
+	 * Also note that the resulting merges will contain the resulting rejections (diffs from the other side
+	 * that will be rejected)
+	 * </p>
+	 * 
+	 * @param diff
+	 *            The difference for which we seek all related ones.
+	 * @param rightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return A non-null set of all diffs related to the given <code>diff</code> when merging in the given
+	 *         direction.
+	 */
+	Set<Diff> getAllResultingMerges(Diff diff, boolean rightToLeft);
+
+	/**
+	 * Retrieves the set of all diffs that will be rejected if the given <code>diff</code> is merged, either
+	 * because of unresolveable conflicts or because of unreachable requirements.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek all opposite ones.
+	 * @param mergeRightToLeft
+	 *            The direction in which we're considering a merge.
+	 * @return A non-null set of all diffs that will be rejected if the given <code>diff</code> is merged in
+	 *         the given direction.
+	 */
+	Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft);
+
+	/**
+	 * Returns the merger registry used for calculating the diff relationships.
+	 * 
+	 * @return The merger registry.
+	 */
+	IMerger.Registry getMergerRegistry();
+
+	/**
+	 * Sets the merger registry used for calculating the diff relationships.
+	 * 
+	 * @param mergerRegistry
+	 *            The merger registry.
+	 */
+	void setMergerRegistry(IMerger.Registry mergerRegistry);
+
+	/**
+	 * Returns the merge criterion considered for calculating the diff relationships.
+	 * 
+	 * @return The merge criterion.
+	 */
+	IMergeCriterion getMergeCriterion();
+
+	/**
+	 * Sets the merge criterion considered for calculating the diff relationships.
+	 * 
+	 * @param mergeCriterion
+	 *            The merger criterion.
+	 */
+	void setMergeCriterion(IMergeCriterion mergeCriterion);
+
+	/**
+	 * Returns the best-fitting merger for the given diff according to the {@link #getMergerRegistry() merger
+	 * registry} and the {@link #getMergeCriterion() merge criterion}.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek the merger.
+	 * @return The best-fitting merger for the given diff or null if no merger fits.
+	 */
+	IMerger2 getMerger(Diff diff);
+
+	/**
+	 * Indicates whether a best-fitting merger for the given diff is available.
+	 * 
+	 * @param diff
+	 *            The difference for which we seek the merger.
+	 * @return Returns true if a best-fitting merger is available, false otherwise.
+	 */
+	boolean hasMerger(Diff diff);
+}