[505654] Support automatic remerging / multiple merges from the same branch

https://bugs.eclipse.org/bugs/show_bug.cgi?id=505654
diff --git a/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters b/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
index c61f87f..20cc2c3 100644
--- a/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
+++ b/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
@@ -192,13 +192,6 @@
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/emf/cdo/spi/common/commit/CDOChangeSetSegment.java" type="org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment">
-        <filter id="337768515">
-            <message_arguments>
-                <message_argument value="org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment"/>
-            </message_arguments>
-        </filter>
-    </resource>
     <resource path="src/org/eclipse/emf/cdo/spi/common/id/AbstractCDOID.java" type="org.eclipse.emf.cdo.spi.common.id.AbstractCDOID">
         <filter id="337682486">
             <message_arguments>
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
index 90cb23c..d281867 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
@@ -30,8 +30,9 @@
    * @since 4.2
    * @noreference This field is not intended to be referenced by clients.
    */
-  public static final int PROTOCOL_VERSION = 33; // CDOCommitInfo.getMergeSource()
+  public static final int PROTOCOL_VERSION = 34; // CDOSessionProtocol.loadMergeData2()
 
+  // public static final int PROTOCOL_VERSION = 33; // CDOCommitInfo.getMergeSource()
   // public static final int PROTOCOL_VERSION = 32; // ROLLBACK_REASON_UNIT_INTEGRITY
   // public static final int PROTOCOL_VERSION = 31; // CDOCommonRepository.isSupportingUnits()
   // public static final int PROTOCOL_VERSION = 30; // UnitOpcode
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CountedTimeProvider.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CountedTimeProvider.java
new file mode 100644
index 0000000..fff86e0
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CountedTimeProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2004-2016 Eike Stepper (Berlin, Germany) 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.common.util;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author Eike Stepper
+ * @since 4.6
+ */
+public final class CountedTimeProvider implements CDOTimeProvider
+{
+  private final AtomicLong counter;
+
+  public CountedTimeProvider()
+  {
+    this(0L);
+  }
+
+  public CountedTimeProvider(long initialValue)
+  {
+    counter = new AtomicLong(initialValue);
+  }
+
+  public long getTimeStamp()
+  {
+    return counter.getAndIncrement();
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CurrentTimeProvider.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CurrentTimeProvider.java
new file mode 100644
index 0000000..4dc8e10
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/util/CurrentTimeProvider.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2004-2016 Eike Stepper (Berlin, Germany) 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.common.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.6
+ */
+public class CurrentTimeProvider implements CDOTimeProvider
+{
+  public static final CDOTimeProvider INSTANCE = new CurrentTimeProvider();
+
+  private CurrentTimeProvider()
+  {
+  }
+
+  public long getTimeStamp()
+  {
+    return System.currentTimeMillis();
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOCommitInfoImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOCommitInfoImpl.java
index 4312270..35b19df 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOCommitInfoImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOCommitInfoImpl.java
@@ -271,8 +271,8 @@
     long t = commitInfo.getTimeStamp();
     String timeStamp = CDOCommonUtil.formatTimeStamp(t) + " (" + t + ")";
 
-    return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}]", timeStamp, commitInfo.getBranch(), //$NON-NLS-1$
-        commitInfo.getUserID(), commitInfo.getComment(), data);
+    return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}, {5}]", timeStamp, commitInfo.getBranch(), //$NON-NLS-1$
+        commitInfo.getUserID(), commitInfo.getComment(), commitInfo.getMergeSource(), data);
   }
 
   public synchronized boolean isCommitDataLoaded()
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/branch/CDOBranchUtil.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/branch/CDOBranchUtil.java
index bace2c2..e6559bd 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/branch/CDOBranchUtil.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/branch/CDOBranchUtil.java
@@ -15,10 +15,13 @@
 import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
 import org.eclipse.emf.cdo.common.branch.CDOBranchPointRange;
 import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
 import org.eclipse.emf.cdo.internal.common.branch.CDOBranchManagerImpl;
 import org.eclipse.emf.cdo.internal.common.branch.CDOBranchPointRangeImpl;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -33,24 +36,7 @@
   /**
    * @since 4.6
    */
-  public static final CDOBranchPoint AUTO_BRANCH_POINT = new CDOBranchPoint()
-  {
-    public long getTimeStamp()
-    {
-      return Long.MIN_VALUE;
-    }
-
-    public CDOBranch getBranch()
-    {
-      return null;
-    }
-
-    @Override
-    public String toString()
-    {
-      return "BranchPoint[AUTO]";
-    }
-  };
+  public static final CDOBranchPoint AUTO_BRANCH_POINT = new AutoBranchPoint();
 
   private CDOBranchUtil()
   {
@@ -66,6 +52,83 @@
     return new CDOBranchPointRangeImpl(startPoint, endPoint);
   }
 
+  /**
+   * @since 4.6
+   */
+  public static void writeRange(CDODataOutput out, CDOBranchPointRange range) throws IOException
+  {
+    out.writeCDOBranchPoint(range.getStartPoint());
+    out.writeCDOBranchPoint(range.getEndPoint());
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static CDOBranchPointRange readRange(CDODataInput in) throws IOException
+  {
+    CDOBranchPoint startPoint = in.readCDOBranchPoint();
+    CDOBranchPoint endPoint = in.readCDOBranchPoint();
+    return createRange(startPoint, endPoint);
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static void writeRangeOrNull(CDODataOutput out, CDOBranchPointRange range) throws IOException
+  {
+    if (range != null)
+    {
+      out.writeBoolean(true);
+      writeRange(out, range);
+    }
+    else
+    {
+      out.writeBoolean(false);
+    }
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static CDOBranchPointRange readRangeOrNull(CDODataInput in) throws IOException
+  {
+    if (in.readBoolean())
+    {
+      return readRange(in);
+    }
+
+    return null;
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static void writeBranchPointOrNull(CDODataOutput out, CDOBranchPoint point) throws IOException
+  {
+    if (point != null)
+    {
+      out.writeBoolean(true);
+      out.writeCDOBranchPoint(point);
+    }
+    else
+    {
+      out.writeBoolean(false);
+    }
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static CDOBranchPoint readBranchPointOrNull(CDODataInput in) throws IOException
+  {
+    if (in.readBoolean())
+    {
+      return in.readCDOBranchPoint();
+    }
+
+    return null;
+  }
+
   public static CDOBranchPoint copyBranchPoint(CDOBranchPoint source)
   {
     return source.getBranch().getPoint(source.getTimeStamp());
@@ -208,4 +271,26 @@
       getPath(branch.getBase(), result);
     }
   }
+
+  /**
+   * @author Eike Stepper
+   */
+  private static final class AutoBranchPoint implements CDOBranchPoint
+  {
+    public long getTimeStamp()
+    {
+      return Long.MIN_VALUE;
+    }
+
+    public CDOBranch getBranch()
+    {
+      return null;
+    }
+
+    @Override
+    public String toString()
+    {
+      return "BranchPoint[AUTO]";
+    }
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDOChangeSetSegment.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDOChangeSetSegment.java
index 4bd5b94..aaf4e49 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDOChangeSetSegment.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDOChangeSetSegment.java
@@ -20,10 +20,11 @@
 /**
  * If the meaning of this type isn't clear, there really should be more of a description here...
  *
+ * @noextend This class is not intended to be subclassed by clients.
  * @author Eike Stepper
  * @since 3.0
  */
-public final class CDOChangeSetSegment implements CDOBranchPoint
+public class CDOChangeSetSegment implements CDOBranchPoint
 {
   private CDOBranchPoint branchPoint;
 
@@ -91,21 +92,99 @@
     return MessageFormat.format("Segment[{0}, {1}, {2}]", getBranch(), getTimeStamp(), endTime); //$NON-NLS-1$
   }
 
-  public static CDOChangeSetSegment[] createFrom(CDOBranchPoint startPoint, CDOBranchPoint endPoint)
+  /**
+   * @since 4.6
+   */
+  public static void handleSegments(CDOBranchPoint endPoint, Handler handler)
   {
-    LinkedList<CDOChangeSetSegment> result = new LinkedList<CDOChangeSetSegment>();
+    long creationTime = endPoint.getBranch().getBranchManager().getMainBranch().getBase().getTimeStamp();
+    handleSegments(creationTime, endPoint, handler);
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static void handleSegments(long startTime, CDOBranchPoint endPoint, Handler handler)
+  {
+    CDOBranch endBranch = endPoint.getBranch();
+
+    for (;;)
+    {
+      CDOBranchPoint base = endBranch.getBase();
+      long timeStamp = base.getTimeStamp();
+      long endTime = endPoint.getTimeStamp();
+
+      if (timeStamp <= startTime)
+      {
+        handler.handleSegment(new CDOChangeSetSegment(endBranch, startTime, endTime));
+        return;
+      }
+
+      if (!handler.handleSegment(new CDOChangeSetSegment(endBranch, timeStamp, endTime)))
+      {
+        return;
+      }
+
+      endPoint = base;
+      endBranch = base.getBranch();
+    }
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static void handleSegments(CDOBranchPoint startPoint, CDOBranchPoint endPoint, Handler handler)
+  {
     CDOBranch startBranch = startPoint.getBranch();
     CDOBranch endBranch = endPoint.getBranch();
 
     while (startBranch != endBranch)
     {
       CDOBranchPoint base = endBranch.getBase();
-      result.addFirst(new CDOChangeSetSegment(endBranch, base.getTimeStamp(), endPoint.getTimeStamp()));
+      if (!handler.handleSegment(new CDOChangeSetSegment(endBranch, base.getTimeStamp(), endPoint.getTimeStamp())))
+      {
+        return;
+      }
+
       endPoint = base;
       endBranch = base.getBranch();
     }
 
-    result.addFirst(new CDOChangeSetSegment(startBranch, startPoint.getTimeStamp(), endPoint.getTimeStamp()));
+    handler.handleSegment(new CDOChangeSetSegment(startBranch, startPoint.getTimeStamp(), endPoint.getTimeStamp()));
+  }
+
+  /**
+   * @since 4.6
+   */
+  public static CDOChangeSetSegment[] createFrom(long startTime, CDOBranchPoint endPoint)
+  {
+    final LinkedList<CDOChangeSetSegment> result = new LinkedList<CDOChangeSetSegment>();
+
+    handleSegments(startTime, endPoint, new Handler()
+    {
+      public boolean handleSegment(CDOChangeSetSegment segment)
+      {
+        result.addFirst(segment);
+        return true;
+      }
+    });
+
+    return result.toArray(new CDOChangeSetSegment[result.size()]);
+  }
+
+  public static CDOChangeSetSegment[] createFrom(CDOBranchPoint startPoint, CDOBranchPoint endPoint)
+  {
+    final LinkedList<CDOChangeSetSegment> result = new LinkedList<CDOChangeSetSegment>();
+
+    handleSegments(startPoint, endPoint, new Handler()
+    {
+      public boolean handleSegment(CDOChangeSetSegment segment)
+      {
+        result.addFirst(segment);
+        return true;
+      }
+    });
+
     return result.toArray(new CDOChangeSetSegment[result.size()]);
   }
 
@@ -124,4 +203,13 @@
 
     return false;
   }
+
+  /**
+   * @author Eike Stepper
+   * @since 4.6
+   */
+  public interface Handler
+  {
+    public boolean handleSegment(CDOChangeSetSegment segment);
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDORevisionAvailabilityInfo.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDORevisionAvailabilityInfo.java
index 51b2b6b..ad12cf7 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDORevisionAvailabilityInfo.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/commit/CDORevisionAvailabilityInfo.java
@@ -15,6 +15,7 @@
 import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
 import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
 
 import java.text.MessageFormat;
@@ -28,13 +29,24 @@
  */
 public final class CDORevisionAvailabilityInfo implements CDORevisionProvider
 {
+  private final Map<CDOID, CDORevisionKey> availableRevisions = CDOIDUtil.createMap();
+
   private CDOBranchPoint branchPoint;
 
-  private Map<CDOID, CDORevisionKey> availableRevisions = CDOIDUtil.createMap();
+  private CDORevisionManager revisionManager;
+
+  /**
+   * @since 4.6
+   */
+  public CDORevisionAvailabilityInfo(CDOBranchPoint branchPoint, CDORevisionManager revisionManager)
+  {
+    this.branchPoint = branchPoint;
+    this.revisionManager = revisionManager;
+  }
 
   public CDORevisionAvailabilityInfo(CDOBranchPoint branchPoint)
   {
-    this.branchPoint = branchPoint;
+    this(branchPoint, null);
   }
 
   public CDOBranchPoint getBranchPoint()
@@ -72,7 +84,13 @@
 
   public CDORevision getRevision(CDOID id)
   {
-    return (CDORevision)availableRevisions.get(id);
+    CDORevision revision = (CDORevision)availableRevisions.get(id);
+    if (revision == null && revisionManager != null)
+    {
+      return revisionManager.getRevision(id, branchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
+    }
+
+    return revision;
   }
 
   @Override
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataInputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataInputImpl.java
index a93b48f..b86d445 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataInputImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataInputImpl.java
@@ -70,6 +70,7 @@
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl;
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDOSetFeatureDeltaImpl;
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDOUnsetFeatureDeltaImpl;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.commit.CDOCommitInfoUtil;
 import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
 import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState;
@@ -284,7 +285,7 @@
       CDOBranch branch = readCDOBranch();
       String userID = readString();
       String comment = readString();
-      CDOBranchPoint mergeSource = readBoolean() ? readCDOBranchPoint() : null;
+      CDOBranchPoint mergeSource = CDOBranchUtil.readBranchPointOrNull(this);
       CDOCommitData commitData = readCDOCommitData();
 
       return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource,
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataOutputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataOutputImpl.java
index 9a26dc9..654c9f8 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataOutputImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/protocol/CDODataOutputImpl.java
@@ -47,6 +47,7 @@
 import org.eclipse.emf.cdo.internal.common.model.CDOTypeImpl;
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDOFeatureDeltaImpl;
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
 import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker;
@@ -229,18 +230,7 @@
       writeCDOBranch(branch);
       writeString(commitInfo.getUserID());
       writeString(commitInfo.getComment());
-
-      CDOBranchPoint mergeSource = commitInfo.getMergeSource();
-      if (mergeSource != null)
-      {
-        writeBoolean(true);
-        writeCDOBranchPoint(mergeSource);
-      }
-      else
-      {
-        writeBoolean(false);
-      }
-
+      CDOBranchUtil.writeBranchPointOrNull(this, commitInfo.getMergeSource());
       writeCDOCommitData(commitInfo);
     }
     else
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
index cbaad54..d9ed8ea 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
@@ -497,9 +497,16 @@
     return send(new LoadChangeSetsRequest(this, ranges));
   }
 
+  @Deprecated
   public Set<CDOID> loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
       CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
   {
+    throw new UnsupportedOperationException();
+  }
+
+  public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
+  {
     return send(new LoadMergeDataRequest(this, targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo));
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
index 4b2784e..8a9d60e 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
@@ -39,6 +39,7 @@
 import org.eclipse.emf.cdo.common.security.CDOPermission;
 import org.eclipse.emf.cdo.internal.net4j.bundle.OM;
 import org.eclipse.emf.cdo.session.CDORepositoryInfo;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 
 import org.eclipse.emf.internal.cdo.object.CDOObjectReferenceImpl;
 import org.eclipse.emf.internal.cdo.view.AbstractCDOView;
@@ -160,16 +161,7 @@
     out.writeLong(getLastUpdateTime());
     out.writeInt(commitNumber);
     out.writeString(commitComment);
-
-    if (commitMergeSource != null)
-    {
-      out.writeBoolean(true);
-      out.writeCDOBranchPoint(commitMergeSource);
-    }
-    else
-    {
-      out.writeBoolean(false);
-    }
+    CDOBranchUtil.writeBranchPointOrNull(out, commitMergeSource);
 
     out.writeInt(locksOnNewObjects.size());
     out.writeInt(idsToUnlock.size());
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java
index c981929..fa3ae85 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java
@@ -15,6 +15,7 @@
 import org.eclipse.emf.cdo.common.protocol.CDODataInput;
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 
 import java.io.IOException;
 
@@ -37,8 +38,7 @@
     out.writeInt(ranges.length);
     for (CDOBranchPointRange range : ranges)
     {
-      out.writeCDOBranchPoint(range.getStartPoint());
-      out.writeCDOBranchPoint(range.getEndPoint());
+      CDOBranchUtil.writeRange(out, range);
     }
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java
index 6a32387..3d23bcd 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java
@@ -18,6 +18,7 @@
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 import org.eclipse.emf.cdo.internal.net4j.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
 
 import java.io.IOException;
@@ -73,7 +74,7 @@
       long timeStamp = in.readLong();
       String userID = in.readString();
       String comment = in.readString();
-      CDOBranchPoint mergeSource = in.readBoolean() ? in.readCDOBranchPoint() : null;
+      CDOBranchPoint mergeSource = CDOBranchUtil.readBranchPointOrNull(in);
 
       try
       {
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java
index 54e25e6..65f3522 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java
@@ -12,6 +12,7 @@
 
 import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
 import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 import org.eclipse.emf.cdo.common.protocol.CDODataInput;
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
@@ -22,8 +23,9 @@
 
 import org.eclipse.net4j.util.om.monitor.OMMonitor;
 
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult;
+
 import java.io.IOException;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -31,7 +33,7 @@
 /**
  * @author Eike Stepper
  */
-public class LoadMergeDataRequest extends CDOClientRequestWithMonitoring<Set<CDOID>>
+public class LoadMergeDataRequest extends CDOClientRequestWithMonitoring<MergeDataResult>
 {
   private CDORevisionAvailabilityInfo targetInfo;
 
@@ -121,22 +123,53 @@
   }
 
   @Override
-  protected Set<CDOID> confirming(CDODataInput in, OMMonitor monitor) throws IOException
+  protected MergeDataResult confirming(CDODataInput in, OMMonitor monitor) throws IOException
   {
-    Set<CDOID> result = new HashSet<CDOID>();
+    MergeDataResult result = new MergeDataResult();
 
-    int size = in.readInt();
-    monitor.begin(size + infos);
+    monitor.begin(1 + infos);
 
     try
     {
-      for (int i = 0; i < size; i++)
+      // Read IDs of objects that are changed only in target.
+      for (;;)
       {
         CDOID id = in.readCDOID();
-        result.add(id);
-        monitor.worked();
+        if (CDOIDUtil.isNull(id))
+        {
+          break;
+        }
+
+        result.getTargetIDs().add(id);
       }
 
+      // Read IDs of objects that are changed in both target and source.
+      for (;;)
+      {
+        CDOID id = in.readCDOID();
+        if (CDOIDUtil.isNull(id))
+        {
+          break;
+        }
+
+        result.getTargetIDs().add(id);
+        result.getSourceIDs().add(id);
+      }
+
+      // Read IDs of objects that are changed only in source.
+      for (;;)
+      {
+        CDOID id = in.readCDOID();
+        if (CDOIDUtil.isNull(id))
+        {
+          break;
+        }
+
+        result.getSourceIDs().add(id);
+      }
+
+      monitor.worked();
+
       if (auto)
       {
         targetBaseInfo.setBranchPoint(in.readCDOBranchPoint());
@@ -151,19 +184,20 @@
         }
       }
 
-      readRevisionAvailabilityInfo(in, targetInfo, result, monitor.fork());
-      readRevisionAvailabilityInfo(in, sourceInfo, result, monitor.fork());
+      readRevisionAvailabilityInfo(in, targetInfo, result.getTargetIDs(), monitor.fork());
+      readRevisionAvailabilityInfo(in, sourceInfo, result.getSourceIDs(), monitor.fork());
 
       if (infos > 2)
       {
-        readRevisionAvailabilityInfo(in, targetBaseInfo, result, monitor.fork());
+        readRevisionAvailabilityInfo(in, targetBaseInfo, result.getTargetIDs(), monitor.fork());
       }
 
       if (infos > 3)
       {
-        readRevisionAvailabilityInfo(in, sourceBaseInfo, result, monitor.fork());
+        readRevisionAvailabilityInfo(in, sourceBaseInfo, result.getSourceIDs(), monitor.fork());
       }
 
+      result.setResultBase(CDOBranchUtil.readBranchPointOrNull(in));
       return result;
     }
     finally
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java
index 5d4b5e0..87698e0 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java
@@ -62,14 +62,7 @@
   @Override
   protected CDOBranchPointRange confirming(CDODataInput in) throws IOException
   {
-    if (in.readBoolean())
-    {
-      CDOBranchPoint point1 = in.readCDOBranchPoint();
-      CDOBranchPoint point2 = in.readCDOBranchPoint();
-      return CDOBranchUtil.createRange(point1, point2);
-    }
-
-    return null;
+    return CDOBranchUtil.readRangeOrNull(in);
   }
 
   @Override
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java
index fe261e7..d770196 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java
@@ -208,7 +208,7 @@
       out.println("import org.eclipse.emf.cdo.session.CDOSession;");
       out.println("import org.eclipse.emf.cdo.tests.AbstractCDOTest;");
       out.println("import org.eclipse.emf.cdo.transaction.CDOTransaction;");
-      out.println("org.eclipse.emf.spi.cdo.DefaultCDOMerger;");
+      out.println("import org.eclipse.emf.spi.cdo.DefaultCDOMerger;");
       out.println();
 
       if (!StringUtil.isEmpty(DESCRIPTION))
@@ -462,16 +462,16 @@
     modifyingObject(transaction, object, featureDelta);
   }
 
-  public synchronized void remergeTransaction(TestRecorderTransaction transaction, CDOBranch source, CDOMerger merger)
+  public synchronized void mergeTransaction(TestRecorderTransaction transaction, CDOBranch source, CDOMerger merger)
   {
-    println(variables.get(transaction) + ".remerge(" + formatBranch(source, false) + ", new " + simpleClassName(merger)
+    println(variables.get(transaction) + ".merge(" + formatBranch(source, false) + ", new " + simpleClassName(merger)
         + "());");
   }
 
-  public synchronized void remergeTransaction(TestRecorderTransaction transaction, CDOBranchPoint source,
+  public synchronized void mergeTransaction(TestRecorderTransaction transaction, CDOBranchPoint source,
       CDOMerger merger)
   {
-    println(variables.get(transaction) + ".remerge(" + formatBranchPoint(source, false) + ", new "
+    println(variables.get(transaction) + ".merge(" + formatBranchPoint(source, false) + ", new "
         + simpleClassName(merger) + "());");
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java
index 768b9cc..a1a9a15 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java
@@ -42,14 +42,14 @@
   @Override
   public CDOChangeSetData merge(CDOBranch source, CDOMerger merger)
   {
-    TestRecorder.INSTANCE.remergeTransaction(this, source, merger);
+    TestRecorder.INSTANCE.mergeTransaction(this, source, merger);
     return super.merge(source, merger);
   }
 
   @Override
   public CDOChangeSetData merge(CDOBranchPoint source, CDOMerger merger)
   {
-    TestRecorder.INSTANCE.remergeTransaction(this, source, merger);
+    TestRecorder.INSTANCE.mergeTransaction(this, source, merger);
     return super.merge(source, merger);
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java
index 55266e7..56955a5 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java
@@ -934,8 +934,10 @@
       builder.append("=0)"); //$NON-NLS-1$
     }
 
+    String sql = builder.toString();
+
     IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
-    IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW);
+    IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.LOW);
     ResultSet resultSet = null;
     Set<CDOID> result = new HashSet<CDOID>();
 
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
index a5890bb..78d7ead 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
@@ -31,6 +31,7 @@
 import org.eclipse.emf.cdo.server.IPermissionManager;
 import org.eclipse.emf.cdo.server.IView;
 import org.eclipse.emf.cdo.server.internal.net4j.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
@@ -128,7 +129,7 @@
     long lastUpdateTime = in.readLong();
     int commitNumber = in.readInt();
     String commitComment = in.readString();
-    CDOBranchPoint commitMergeSource = in.readBoolean() ? in.readCDOBranchPoint() : null;
+    CDOBranchPoint commitMergeSource = CDOBranchUtil.readBranchPointOrNull(in);
 
     CDOLockState[] locksOnNewObjects = new CDOLockState[in.readInt()];
     CDOID[] idsToUnlock = new CDOID[in.readInt()];
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadChangeSetsIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadChangeSetsIndication.java
index 4e6bf5e..8e1640c 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadChangeSetsIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadChangeSetsIndication.java
@@ -40,9 +40,7 @@
     ranges = new CDOBranchPointRange[size];
     for (int i = 0; i < ranges.length; i++)
     {
-      CDOBranchPoint startPoint = in.readCDOBranchPoint();
-      CDOBranchPoint endPoint = in.readCDOBranchPoint();
-      ranges[i] = CDOBranchUtil.createRange(startPoint, endPoint);
+      ranges[i] = CDOBranchUtil.readRange(in);
     }
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadCommitInfosIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadCommitInfosIndication.java
index cc3cca4..21fe25e 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadCommitInfosIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadCommitInfosIndication.java
@@ -11,12 +11,12 @@
 package org.eclipse.emf.cdo.server.internal.net4j.protocol;
 
 import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
 import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
 import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
 import org.eclipse.emf.cdo.common.protocol.CDODataInput;
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
 
 import org.eclipse.net4j.util.WrappedException;
@@ -69,17 +69,7 @@
             out.writeLong(commitInfo.getTimeStamp());
             out.writeString(commitInfo.getUserID());
             out.writeString(commitInfo.getComment());
-
-            CDOBranchPoint mergeSource = commitInfo.getMergeSource();
-            if (mergeSource != null)
-            {
-              out.writeBoolean(true);
-              out.writeCDOBranchPoint(mergeSource);
-            }
-            else
-            {
-              out.writeBoolean(false);
-            }
+            CDOBranchUtil.writeBranchPointOrNull(out, commitInfo.getMergeSource());
           }
           catch (IOException ex)
           {
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadMergeDataIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadMergeDataIndication.java
index ff6eaf4..dd1ca82 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadMergeDataIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadMergeDataIndication.java
@@ -24,6 +24,8 @@
 
 import org.eclipse.net4j.util.om.monitor.OMMonitor;
 
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult;
+
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashSet;
@@ -124,20 +126,44 @@
 
     try
     {
-      // if (auto)
-      // {
-      // sourceBaseInfo = new CDORevisionAvailabilityInfo(CDOBranchUtil.AUTO_BRANCH_POINT);
-      // }
-
       InternalRepository repository = getRepository();
-      Set<CDOID> ids = repository.getMergeData(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo, monitor.fork());
+      MergeDataResult result = repository.getMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo,
+          monitor.fork());
 
-      out.writeInt(ids.size());
-      for (CDOID id : ids)
+      Set<CDOID> targetIDs = result.getTargetIDs();
+      Set<CDOID> targetAndSourceIDs = new HashSet<CDOID>();
+      Set<CDOID> sourceIDs = result.getSourceIDs();
+
+      // Write IDs of objects that are changed only in target.
+      for (CDOID id : targetIDs)
+      {
+        if (sourceIDs.remove(id))
+        {
+          targetAndSourceIDs.add(id);
+        }
+        else
+        {
+          out.writeCDOID(id);
+        }
+      }
+
+      out.writeCDOID(null);
+
+      // Write IDs of objects that are changed in both target and source.
+      for (CDOID id : targetAndSourceIDs)
       {
         out.writeCDOID(id);
       }
 
+      out.writeCDOID(null);
+
+      // Write IDs of objects that are changed only in source.
+      for (CDOID id : sourceIDs)
+      {
+        out.writeCDOID(id);
+      }
+
+      out.writeCDOID(null);
       monitor.worked();
 
       if (auto)
@@ -168,6 +194,8 @@
       {
         writeRevisionAvailabilityInfo(out, sourceBaseInfo, writtenRevisions, monitor.fork());
       }
+
+      CDOBranchUtil.writeBranchPointOrNull(out, result.getResultBase());
     }
     finally
     {
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadObjectLifetimeIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadObjectLifetimeIndication.java
index c1fd4d3..9fbf296 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadObjectLifetimeIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LoadObjectLifetimeIndication.java
@@ -17,6 +17,7 @@
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 import org.eclipse.emf.cdo.server.internal.net4j.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
 
 import org.eclipse.net4j.util.om.trace.ContextTracer;
@@ -60,15 +61,6 @@
   {
     InternalCDORevisionManager revisionManager = getRepository().getRevisionManager();
     CDOBranchPointRange range = revisionManager.getObjectLifetime(id, branchPoint);
-    if (range != null)
-    {
-      out.writeBoolean(true);
-      out.writeCDOBranchPoint(range.getStartPoint());
-      out.writeCDOBranchPoint(range.getEndPoint());
-    }
-    else
-    {
-      out.writeBoolean(false);
-    }
+    CDOBranchUtil.writeRangeOrNull(out, range);
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo.server/.settings/.api_filters b/plugins/org.eclipse.emf.cdo.server/.settings/.api_filters
index 8c7c4e4..32b0776 100644
--- a/plugins/org.eclipse.emf.cdo.server/.settings/.api_filters
+++ b/plugins/org.eclipse.emf.cdo.server/.settings/.api_filters
@@ -386,12 +386,6 @@
     <resource path="src/org/eclipse/emf/cdo/spi/server/InternalRepository.java" type="org.eclipse.emf.cdo.spi.server.InternalRepository">
         <filter id="571473929">
             <message_arguments>
-                <message_argument value="RevisionLoader"/>
-                <message_argument value="InternalRepository"/>
-            </message_arguments>
-        </filter>
-        <filter id="571473929">
-            <message_arguments>
                 <message_argument value="RevisionLoader2"/>
                 <message_argument value="InternalRepository"/>
             </message_arguments>
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
index 4a83ddc..d632dca 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
@@ -46,6 +46,8 @@
 import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
 import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
 import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.common.util.CDOTimeProvider;
+import org.eclipse.emf.cdo.common.util.CurrentTimeProvider;
 import org.eclipse.emf.cdo.common.util.RepositoryStateChangedEvent;
 import org.eclipse.emf.cdo.common.util.RepositoryTypeChangedEvent;
 import org.eclipse.emf.cdo.eresource.EresourcePackage;
@@ -121,6 +123,7 @@
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult;
 
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -179,6 +182,8 @@
 
   private long optimisticLockingTimeout = 10000L;
 
+  private CDOTimeProvider timeProvider;
+
   /**
    * Must not be thread-bound to support XA commits.
    */
@@ -938,6 +943,17 @@
     return accessor;
   }
 
+  public CDOTimeProvider getTimeProvider()
+  {
+    return timeProvider;
+  }
+
+  public void setTimeProvider(CDOTimeProvider timeProvider)
+  {
+    checkInactive();
+    this.timeProvider = timeProvider;
+  }
+
   public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext)
   {
     if (considerCommitContext)
@@ -1336,7 +1352,7 @@
 
   public long getTimeStamp()
   {
-    return System.currentTimeMillis();
+    return timeProvider.getTimeStamp();
   }
 
   public Set<Handler> getHandlers()
@@ -1543,9 +1559,19 @@
     return CDORevisionUtil.createChangeSetData(ids, startPoint, endPoint, revisionManager);
   }
 
+  @Deprecated
   public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
       CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor)
   {
+    MergeDataResult result = getMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo, monitor);
+    Set<CDOID> ids = result.getTargetIDs();
+    ids.addAll(result.getSourceIDs());
+    return ids;
+  }
+
+  public MergeDataResult getMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor)
+  {
     CDOBranchPoint target = targetInfo.getBranchPoint();
     CDOBranchPoint source = sourceInfo.getBranchPoint();
 
@@ -1554,23 +1580,28 @@
     try
     {
       IStoreAccessor accessor = StoreThreadLocal.getAccessor();
-      Set<CDOID> ids = new HashSet<CDOID>();
+
+      MergeDataResult result = new MergeDataResult();
+      Set<CDOID> targetIDs = result.getTargetIDs();
+      Set<CDOID> sourceIDs = result.getSourceIDs();
 
       if (targetBaseInfo == null && sourceBaseInfo == null)
       {
         if (CDOBranchUtil.isContainedBy(source, target))
         {
-          ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target)));
+          // This is a "compare" case, see CDOSessionImpl.compareRevisions().
+          targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target)));
         }
         else if (CDOBranchUtil.isContainedBy(target, source))
         {
-          ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source)));
+          // This is a "compare" case, see CDOSessionImpl.compareRevisions().
+          targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source)));
         }
         else
         {
           CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source);
-          ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target)));
-          ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source)));
+          targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target)));
+          sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source)));
         }
       }
       else
@@ -1584,35 +1615,41 @@
           targetSegments = CDOChangeSetSegment.createFrom(ancestor, target);
           sourceSegments = CDOChangeSetSegment.createFrom(ancestor, source);
 
-          for (int i = targetSegments.length - 1; i >= 0; --i)
+          CDOBranchPoint targetBase = ancestor;
+          CDOBranchPoint sourceBase = ancestor;
+          long ancestorTime = ancestor.getTimeStamp();
+
+          CDOBranchPointRange latestTargetMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime);
+          if (latestTargetMerge != null)
           {
-            CDOChangeSetSegment targetSegment = targetSegments[i];
-            CDOBranch branch = targetSegment.getBranch();
-            long startTime = targetSegment.getTimeStamp();
-            long endTime = targetSegment.getEndTime();
+            targetBase = latestTargetMerge.getEndPoint();
+            sourceBase = latestTargetMerge.getStartPoint();
 
-            while (endTime > startTime || endTime == CDOBranchPoint.UNSPECIFIED_DATE)
+            if (!sourceBase.equals(ancestor))
             {
-              CDOCommitInfo commitInfo = commitInfoManager.getCommitInfo(branch, endTime, false);
-              if (commitInfo == null)
-              {
-                break;
-              }
-
-              CDOBranchPoint mergeSource = commitInfo.getMergeSource();
-              if (mergeSource != null && CDOChangeSetSegment.contains(sourceSegments, mergeSource))
-              {
-                targetSegments = CDOChangeSetSegment.createFrom(commitInfo, target);
-                sourceSegments = CDOChangeSetSegment.createFrom(mergeSource, source);
-                break;
-              }
-
-              endTime = commitInfo.getTimeStamp() - 1;
+              sourceSegments = CDOChangeSetSegment.createFrom(sourceBase, source);
             }
           }
 
-          targetBaseInfo.setBranchPoint(CDOBranchUtil.copyBranchPoint(targetSegments[0]));
-          sourceBaseInfo.setBranchPoint(CDOBranchUtil.copyBranchPoint(sourceSegments[0]));
+          CDOBranchPointRange latestSourceMerge = getLatestMerge(sourceSegments, targetSegments, ancestorTime);
+          if (latestSourceMerge != null)
+          {
+            CDOBranchPoint mergeSource = latestSourceMerge.getStartPoint();
+            if (targetBase.getTimeStamp() < mergeSource.getTimeStamp())
+            {
+              targetBase = mergeSource;
+            }
+
+            result.setResultBase(sourceBase);
+          }
+
+          if (!targetBase.equals(ancestor))
+          {
+            targetSegments = CDOChangeSetSegment.createFrom(targetBase, target);
+          }
+
+          targetBaseInfo.setBranchPoint(targetBase);
+          sourceBaseInfo.setBranchPoint(sourceBase);
         }
         else
         {
@@ -1621,24 +1658,24 @@
           sourceSegments = CDOChangeSetSegment.createFrom(sourceBaseInfoToUse.getBranchPoint(), source);
         }
 
-        ids.addAll(accessor.readChangeSet(monitor.fork(), targetSegments));
-        ids.addAll(accessor.readChangeSet(monitor.fork(), sourceSegments));
+        targetIDs.addAll(accessor.readChangeSet(monitor.fork(), targetSegments));
+        sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), sourceSegments));
       }
 
-      loadMergeData(ids, targetInfo, monitor.fork());
-      loadMergeData(ids, sourceInfo, monitor.fork());
+      loadMergeData(targetIDs, targetInfo, monitor.fork());
+      loadMergeData(sourceIDs, sourceInfo, monitor.fork());
 
       if (targetBaseInfo != null)
       {
-        loadMergeData(ids, targetBaseInfo, monitor.fork());
+        loadMergeData(targetIDs, targetBaseInfo, monitor.fork());
       }
 
       if (sourceBaseInfo != null && !targetBaseInfo.getBranchPoint().equals(sourceBaseInfo.getBranchPoint()))
       {
-        loadMergeData(ids, sourceBaseInfo, monitor.fork());
+        loadMergeData(sourceIDs, sourceBaseInfo, monitor.fork());
       }
 
-      return ids;
+      return result;
     }
     finally
     {
@@ -1646,6 +1683,66 @@
     }
   }
 
+  private CDOBranchPointRange getLatestMerge(CDOChangeSetSegment[] targetSegments, CDOChangeSetSegment[] sourceSegments,
+      long ancestorTime)
+  {
+    for (int i = targetSegments.length - 1; i >= 0; --i)
+    {
+      CDOChangeSetSegment targetSegment = targetSegments[i];
+      CDOBranch targetBranch = targetSegment.getBranch();
+      long startTime = targetSegment.getTimeStamp();
+      long endTime = targetSegment.getEndTime();
+
+      while (endTime > startTime || endTime == CDOBranchPoint.UNSPECIFIED_DATE)
+      {
+        CDOCommitInfo commitInfo = commitInfoManager.getCommitInfo(targetBranch, endTime, false);
+        if (commitInfo == null)
+        {
+          break;
+        }
+
+        long timeStamp = commitInfo.getTimeStamp();
+        if (timeStamp <= startTime)
+        {
+          break;
+        }
+
+        CDOBranchPoint mergeSource = getMergeSource(commitInfo, sourceSegments, ancestorTime);
+        if (mergeSource != null)
+        {
+          CDOBranchPoint endPoint = CDOBranchUtil.copyBranchPoint(commitInfo);
+          return CDOBranchUtil.createRange(mergeSource, endPoint);
+        }
+
+        endTime = timeStamp - 1;
+      }
+    }
+
+    return null;
+  }
+
+  private CDOBranchPoint getMergeSource(CDOCommitInfo commitInfo, CDOChangeSetSegment[] sourceSegments,
+      long ancestorTime)
+  {
+    CDOBranchPoint mergeSource = commitInfo.getMergeSource();
+    if (mergeSource != null)
+    {
+      if (CDOChangeSetSegment.contains(sourceSegments, mergeSource))
+      {
+        return mergeSource;
+      }
+
+      CDOChangeSetSegment[] targetSegments = CDOChangeSetSegment.createFrom(ancestorTime, mergeSource);
+      CDOBranchPointRange latestMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime);
+      if (latestMerge != null)
+      {
+        return latestMerge.getStartPoint();
+      }
+    }
+
+    return null;
+  }
+
   private void loadMergeData(Set<CDOID> ids, CDORevisionAvailabilityInfo info, OMMonitor monitor)
   {
     int size = ids.size();
@@ -2334,6 +2431,11 @@
     @Override
     protected void doBeforeActivate() throws Exception
     {
+      if (getTimeProvider() == null)
+      {
+        setTimeProvider(createTimeProvider());
+      }
+
       if (getPackageRegistry(false) == null)
       {
         setPackageRegistry(createPackageRegistry());
@@ -2382,6 +2484,11 @@
       super.doBeforeActivate();
     }
 
+    protected CDOTimeProvider createTimeProvider()
+    {
+      return CurrentTimeProvider.INSTANCE;
+    }
+
     protected InternalCDOPackageRegistry createPackageRegistry()
     {
       return new CDOPackageRegistryImpl();
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
index 01533b5..b02700c 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
@@ -601,6 +601,12 @@
     throw new UnsupportedOperationException();
   }
 
+  public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
+  {
+    throw new UnsupportedOperationException();
+  }
+
   public Map<CDORevision, CDOPermission> loadPermissions(InternalCDORevision[] revisions)
   {
     throw new UnsupportedOperationException();
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java
index 25e32d6..3580259 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java
@@ -290,11 +290,13 @@
   {
     InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager();
 
-    if (startTime == endTime && endTime > CDOBranchPoint.UNSPECIFIED_DATE)
+    // Optimize the getCommitInfo(timeStamp) case.
+    if (startTime == endTime && startTime > CDOBranchPoint.UNSPECIFIED_DATE)
     {
       int index = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime));
       if (index >= 0)
       {
+        // Found.
         CommitInfo commitInfo = commitInfos.get(index);
         commitInfo.handle(manager, handler);
       }
@@ -302,7 +304,10 @@
       return;
     }
 
+    boolean counting = endTime < CDOBranchPoint.UNSPECIFIED_DATE;
     int count = CDOCommitInfoUtil.decodeCount(endTime);
+    boolean forward = counting ? count > 0 : endTime > startTime;
+
     int startIndex;
     if (startTime == CDOBranchPoint.UNSPECIFIED_DATE)
     {
@@ -311,35 +316,19 @@
     else
     {
       startIndex = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime));
-    }
-
-    boolean match = true;
-    if (startIndex < 0)
-    {
-      startIndex = -(startIndex + 1);
-      match = false;
-    }
-
-    boolean counting = false;
-    boolean backward = false;
-    if (endTime < CDOBranchPoint.UNSPECIFIED_DATE)
-    {
-      counting = true;
-      if (count < 0)
+      if (startIndex >= 0)
       {
-        backward = true;
-        if (!match)
+        // Found.
+        if (!forward)
         {
-          --startIndex;
+          ++startIndex;
         }
       }
-    }
-    else
-    {
-      if (endTime <= startTime)
+      else
       {
-        backward = true;
-        if (!match)
+        // Not found.
+        startIndex = -(startIndex + 1); // Insertion point.
+        if (forward)
         {
           --startIndex;
         }
@@ -347,17 +336,7 @@
     }
 
     ListIterator<CommitInfo> listIterator = commitInfos.listIterator(startIndex);
-    Iterator<CommitInfo> iterator = new BidirectionalIterator<CommitInfo>(listIterator, backward);
-
-    if (counting)
-    {
-      iterator = new LimitedIterator<CommitInfo>(iterator, Math.abs(count));
-    }
-    else if (startTime != CDOBranchPoint.UNSPECIFIED_DATE || endTime != CDOBranchPoint.UNSPECIFIED_DATE)
-    {
-      Predicate<CDOTimeProvider> predicate = backward ? new DownTo(endTime) : new UpTo(endTime);
-      iterator = new PredicateIterator<CommitInfo>(iterator, predicate);
-    }
+    Iterator<CommitInfo> iterator = new BidirectionalIterator<CommitInfo>(listIterator, !forward);
 
     if (branch != null)
     {
@@ -371,6 +350,16 @@
       };
     }
 
+    if (counting)
+    {
+      iterator = new LimitedIterator<CommitInfo>(iterator, Math.abs(count));
+    }
+    else if (startTime != CDOBranchPoint.UNSPECIFIED_DATE || endTime != CDOBranchPoint.UNSPECIFIED_DATE)
+    {
+      Predicate<CDOTimeProvider> predicate = forward ? new UpTo(endTime) : new DownTo(endTime);
+      iterator = new PredicateIterator<CommitInfo>(iterator, predicate);
+    }
+
     while (iterator.hasNext())
     {
       CommitInfo commitInfo = iterator.next();
@@ -1458,8 +1447,8 @@
     @Override
     public String toString()
     {
-      return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}]", branch, getTimeStamp(), previousTimeStamp,
-          userID, comment);
+      return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}, {5}]", branch, getTimeStamp(), previousTimeStamp,
+          userID, comment, mergeSource);
     }
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
index 5d91fd3..6f5ff7b 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
@@ -21,6 +21,7 @@
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.util.CDOTimeProvider;
 import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
 import org.eclipse.emf.cdo.server.IRepository;
 import org.eclipse.emf.cdo.server.IStoreAccessor;
@@ -47,6 +48,7 @@
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult;
 
 import java.io.IOException;
@@ -84,6 +86,16 @@
   public void setBranchManager(InternalCDOBranchManager branchManager);
 
   /**
+   * @since 4.6
+   */
+  public CDOTimeProvider getTimeProvider();
+
+  /**
+   * @since 4.6
+   */
+  public void setTimeProvider(CDOTimeProvider timeProvider);
+
+  /**
    * @since 4.1
    */
   public Semaphore getPackageRegistryCommitLock();
@@ -236,11 +248,19 @@
 
   /**
    * @since 4.0
+   * @deprecated as of 4.6 use {@link #getMergeData2(CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, OMMonitor)}.
    */
+  @Deprecated
   public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
       CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor);
 
   /**
+   * @since 4.6
+   */
+  public MergeDataResult getMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor);
+
+  /**
    * @since 4.0
    */
   public void queryLobs(List<byte[]> ids);
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CommitInfoTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CommitInfoTest.java
index 91c3313..032c6ec 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CommitInfoTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CommitInfoTest.java
@@ -28,6 +28,7 @@
 import org.eclipse.emf.cdo.tests.config.impl.SessionConfig;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 
+import org.eclipse.net4j.util.io.IOUtil;
 import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
 import org.eclipse.net4j.util.om.monitor.OMMonitor;
 import org.eclipse.net4j.util.security.PasswordCredentials;
@@ -943,7 +944,9 @@
     {
       resource.getContents().add(getModel1Factory().createProduct1());
       transaction.setCommitComment("Commit " + i);
-      expected.add(transaction.commit());
+      CDOCommitInfo commit = transaction.commit();
+      expected.add(commit);
+      IOUtil.OUT().println(expected.get(i).getTimeStamp());
     }
 
     final int LOAD = 10;
@@ -953,6 +956,11 @@
 
     List<CDOCommitInfo> infos = handler.getInfos();
 
+    for (int i = 0; i < LOAD; i++)
+    {
+      IOUtil.OUT().println(expected.get(i).getTimeStamp() + " -> " + infos.get(i).getTimeStamp());
+    }
+
     assertEquals(LOAD, infos.size()); // Initial root resource commit + COMMITS
     for (int i = 0; i < LOAD; i++)
     {
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ConflictResolverTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ConflictResolverTest.java
index 0ce7a95..7a6452a 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ConflictResolverTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ConflictResolverTest.java
@@ -152,7 +152,6 @@
     commitAndSync(transaction2, transaction1);
   }
 
-  // @Skips(IConfig.EFFORT_MERGING)
   public void testMergeLocalChangesPerFeature_Bug2() throws Exception
   {
     CDOSession session = openSession();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337054_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337054_Test.java
index 4335e24..bd4acc2 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337054_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337054_Test.java
@@ -26,9 +26,9 @@
  */
 public class Bugzilla_337054_Test extends AbstractCDOTest
 {
-  private final static String RESOURCE_NAME = "/337054";
+  private static final String RESOURCE_NAME = "/337054";
 
-  private int testedListSize = 3;
+  private static final int LIST_SIZE = 3;
 
   @Requires({ IRepositoryConfig.CAPABILITY_BRANCHING, IRepositoryConfig.CAPABILITY_RESTARTABLE })
   public void testCDOElementProxies() throws Exception
@@ -39,51 +39,39 @@
     CDOTransaction mainTransaction = session.openTransaction();
     CDOResource resource = mainTransaction.createResource(getResourcePath(RESOURCE_NAME));
 
-    // fill up resource contents if empty
-    int actualSize = resource.getContents().size();
-    if (actualSize < testedListSize)
+    msg("Filling up list...");
+    for (int i = 0; i < LIST_SIZE; i++)
     {
-      msg("Filling up list...");
-
-      for (int i = actualSize; i < testedListSize; i++)
-      {
-        Company company = getModel1Factory().createCompany();
-        company.setName("TestCompany_" + i);
-        resource.getContents().add(company);
-
-        resource.getContents().add(company);
-
-        actualSize++;
-      }
-
-      msg("Committing data...");
-      mainTransaction.commit();
+      Company company = getModel1Factory().createCompany();
+      company.setName("TestCompany_" + i);
+      resource.getContents().add(company);
     }
 
+    msg("Committing data...");
+    mainTransaction.commit();
     mainTransaction.close();
 
-    msg("Creating a branch with a new element: ");
-
+    msg("Creating a branch with a new element...");
     String branchName = String.valueOf(System.currentTimeMillis());
     CDOBranch branch = session.getBranchManager().getMainBranch().createBranch(branchName);
 
     Company branchCompany = getModel1Factory().createCompany();
-    branchCompany.setName("TestCompany_" + actualSize);
+    branchCompany.setName("TestCompany_" + LIST_SIZE);
 
     CDOTransaction branchTransaction = session.openTransaction(branch);
-    CDOResource branchRootResource = branchTransaction.getOrCreateResource(getResourcePath(RESOURCE_NAME));
-    branchRootResource.getContents().add(branchCompany);
+    CDOResource branchResource = branchTransaction.getResource(getResourcePath(RESOURCE_NAME));
+    branchResource.getContents().add(branchCompany);
 
     branchTransaction.commit();
     branchTransaction.close();
 
-    // restart repository
+    // Restart repository.
     restartRepository();
 
     session = openSession();
     session.options().setCollectionLoadingPolicy(CDOUtil.createCollectionLoadingPolicy(0, 300));
 
-    // merge
+    // Merge.
     mainTransaction = session.openTransaction(session.getBranchManager().getMainBranch());
     branch = session.getBranchManager().getMainBranch().getBranch(branchName);
 
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485487_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485487_Test.java
index d724ae6..47ab6c2 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485487_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485487_Test.java
@@ -17,6 +17,7 @@
 import org.eclipse.emf.cdo.spi.server.InternalRepository;
 import org.eclipse.emf.cdo.spi.server.InternalTransaction;
 import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.config.IConfig;
 import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 import org.eclipse.emf.cdo.util.CommitException;
@@ -79,6 +80,7 @@
     map.put(RepositoryConfig.PROP_TEST_ENABLE_SERVER_BROWSER, true);
   }
 
+  @Skips(IConfig.CAPABILITY_SANITIZE_TIMEOUT)
   @CleanRepositoriesBefore(reason = "Isolated repository needed")
   @CleanRepositoriesAfter(reason = "Isolated repository needed")
   public void testTimeoutDuringCommit() throws Exception
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_505654_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_505654_Test.java
new file mode 100644
index 0000000..9683d2f
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_505654_Test.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.tests.bugzilla;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.config.IRepositoryConfig;
+import org.eclipse.emf.cdo.tests.config.IRepositoryConfig.CountedTime;
+import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires;
+import org.eclipse.emf.cdo.tests.model1.Company;
+import org.eclipse.emf.cdo.transaction.CDOMerger;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOUtil;
+import org.eclipse.emf.cdo.util.CommitException;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Bug 505654 - Support automatic re-merging / multiple merges from the same branch
+ *
+ * @author Eike Stepper
+ */
+@Requires(IRepositoryConfig.CAPABILITY_BRANCHING)
+@CountedTime
+public class Bugzilla_505654_Test extends AbstractCDOTest
+{
+  private CDOTransaction lastCommit;
+
+  private CDOTransaction leftTransaction;
+
+  private CDOTransaction rightTransaction;
+
+  private Company leftCompany;
+
+  private Company rightCompany;
+
+  private void assertIDs(List<CDOID> actual, CDOID... expected)
+  {
+    assertEquals(Arrays.asList(expected), actual);
+  }
+
+  private static void dump(String indent, EObject object, List<CDOID> ids)
+  {
+    for (EObject child : object.eContents())
+    {
+      System.out.println(indent + child);
+      ids.add(CDOUtil.getCDOObject(child).cdoID());
+      dump(indent + " ", child);
+    }
+  }
+
+  private static List<CDOID> dump(String indent, EObject object)
+  {
+    List<CDOID> ids = new ArrayList<CDOID>();
+    dump(indent, object, ids);
+    return ids;
+  }
+
+  private List<CDOID> leftCommit() throws CommitException
+  {
+    if (lastCommit == leftTransaction)
+    {
+      System.out.println();
+    }
+    else
+    {
+      lastCommit = leftTransaction;
+    }
+
+    CDOCommitInfo commit = commitAndSync(leftTransaction, rightTransaction);
+    System.out.println("COMMIT " + commit.getTimeStamp());
+    return dump("", leftCompany);
+  }
+
+  private List<CDOID> rightCommit() throws CommitException
+  {
+    if (lastCommit == rightTransaction)
+    {
+      System.out.println();
+    }
+    else
+    {
+      lastCommit = rightTransaction;
+    }
+
+    CDOCommitInfo commit = commitAndSync(rightTransaction, leftTransaction);
+    System.out.println("                              COMMIT " + commit.getTimeStamp());
+    return dump("                              ", rightCompany);
+  }
+
+  private List<CDOID> leftMerge() throws CommitException
+  {
+    System.out.println("             <----------------");
+    CDOMerger merger = new DefaultCDOMerger.PerFeature.ManyValued();
+    leftTransaction.merge(rightTransaction.getBranch(), merger);
+    lastCommit = null;
+    return leftCommit();
+  }
+
+  private List<CDOID> rightMerge() throws CommitException
+  {
+    System.out.println("             ---------------->");
+    CDOMerger merger = new DefaultCDOMerger.PerFeature.ManyValued();
+    rightTransaction.merge(leftTransaction.getBranch(), merger);
+    lastCommit = null;
+    return rightCommit();
+  }
+
+  private CDOID leftAdd(EClass eClass) throws CommitException
+  {
+    return add(eClass, true);
+  }
+
+  private CDOID rightAdd(EClass eClass) throws CommitException
+  {
+    return add(eClass, false);
+  }
+
+  private CDOID add(EClass eClass, boolean left) throws CommitException
+  {
+    EObject object = EcoreUtil.create(eClass);
+    for (EReference containment : getModel1Package().getCompany().getEAllContainments())
+    {
+      if (containment.getEReferenceType() == eClass)
+      {
+        Company company = left ? leftCompany : rightCompany;
+
+        @SuppressWarnings("unchecked")
+        EList<EObject> list = (EList<EObject>)company.eGet(containment);
+        list.add(object);
+        break;
+      }
+    }
+
+    if (left)
+    {
+      leftCommit();
+    }
+    else
+    {
+      rightCommit();
+    }
+
+    return CDOUtil.getCDOObject(object).cdoID();
+  }
+
+  @Override
+  protected void doSetUp() throws Exception
+  {
+    super.doSetUp();
+    CDOSession session = openSession();
+
+    leftTransaction = session.openTransaction();
+    CDOResource leftResource = leftTransaction.createResource(getResourcePath("/model1"));
+    leftCompany = getModel1Factory().createCompany();
+    leftResource.getContents().add(leftCompany);
+    CDOCommitInfo commit = leftTransaction.commit();
+
+    System.out.println("COMMIT " + commit.getTimeStamp());
+    System.out.println(leftCompany);
+
+    CDOBranch rightBranch = CDOUtil.createBranch(commit, "branch1");
+    rightTransaction = session.openTransaction(rightBranch);
+    CDOResource rightResource = rightTransaction.getResource(getResourcePath("/model1"));
+    rightCompany = (Company)rightResource.getContents().get(0);
+  }
+
+  public void testMultipleRemerges() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, customer1);
+
+    CDOID customer2 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier2 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, customer1, customer2);
+
+    CDOID customer3 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier3 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, supplier3, customer1, customer2, customer3);
+  }
+
+  public void testMultipleRemerges_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b);
+
+    CDOID customer2 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer2b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer2b, customer2, customer1b);
+
+    CDOID customer3 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer3b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer2b, customer3b, customer3, customer2, customer1b);
+  }
+
+  public void testCrossMerge() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, customer1);
+    assertIDs(rightMerge(), supplier1, customer1);
+  }
+
+  public void testCrossMerge_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b);
+    assertIDs(rightMerge(), customer1, customer1b);
+  }
+
+  public void testCrossMergeAndRemerge() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(rightMerge(), supplier1, customer1);
+
+    CDOID supplier2 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, customer1);
+  }
+
+  public void testCrossMergeAndRemerge_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(rightMerge(), customer1b, customer1);
+
+    CDOID customer2b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1b, customer1, customer2b);
+  }
+
+  public void testMergeAndCrossMergeAndRemerge() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, customer1);
+    assertIDs(rightMerge(), supplier1, customer1);
+
+    CDOID supplier2 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, customer1);
+  }
+
+  public void testMergeAndCrossMergeAndRemerge_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b);
+    assertIDs(rightMerge(), customer1, customer1b);
+
+    CDOID customer2b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b, customer2b);
+  }
+
+  public void testCrossMergeAndMultipleRemerges() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, customer1);
+    assertIDs(rightMerge(), supplier1, customer1);
+
+    CDOID supplier2 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, customer1);
+
+    CDOID supplier3 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, supplier3, customer1);
+  }
+
+  public void testCrossMergeAndMultipleRemerges_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b);
+    assertIDs(rightMerge(), customer1, customer1b);
+
+    CDOID customer2b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b, customer2b);
+
+    CDOID customer3b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b, customer2b, customer3b);
+  }
+
+  public void testCrossMergeAndAdditionsAndRemerges() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier1 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, customer1);
+    assertIDs(rightMerge(), supplier1, customer1);
+
+    CDOID customer2 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier2 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, customer1, customer2);
+
+    CDOID customer3 = leftAdd(getModel1Package().getCustomer());
+    CDOID supplier3 = rightAdd(getModel1Package().getSupplier());
+    assertIDs(leftMerge(), supplier1, supplier2, supplier3, customer1, customer2, customer3);
+  }
+
+  public void testCrossMergeAndAdditionsAndRemerges_SameList() throws Exception
+  {
+    CDOID customer1 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer1b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b);
+    assertIDs(rightMerge(), customer1, customer1b);
+
+    CDOID customer2 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer2b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b, customer2, customer2b);
+
+    CDOID customer3 = leftAdd(getModel1Package().getCustomer());
+    CDOID customer3b = rightAdd(getModel1Package().getCustomer());
+    assertIDs(leftMerge(), customer1, customer1b, customer2, customer3b, customer3, customer2b);
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IConfig.java
index 677699a..3c26cc9 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IConfig.java
@@ -23,7 +23,7 @@
 
   public static final String CAPABILITY_UNAVAILABLE = "___UNAVAILABLE___";
 
-  public static final String EFFORT_MERGING = "___MERGING___";
+  public static final String CAPABILITY_SANITIZE_TIMEOUT = "___SANITIZE_TIMEOUT___";
 
   public String getName();
 
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IRepositoryConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IRepositoryConfig.java
index 5336bbf..69ef8ec 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IRepositoryConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/IRepositoryConfig.java
@@ -72,4 +72,11 @@
   public @interface CallAddRepository
   {
   }
+
+  @Inherited
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ ElementType.TYPE, ElementType.METHOD })
+  public @interface CountedTime
+  {
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
index b885562..f8cf700 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
@@ -93,6 +93,8 @@
 
   private IScenario scenario;
 
+  private boolean defaultScenario;
+
   private Properties homeProperties;
 
   private Map<String, Object> testProperties;
@@ -106,10 +108,16 @@
     return Config.getExecutorService();
   }
 
+  public boolean hasDefaultScenario()
+  {
+    return defaultScenario;
+  }
+
   public synchronized IScenario getScenario()
   {
     if (scenario == null)
     {
+      defaultScenario = true;
       setScenario(getDefaultScenario());
     }
 
@@ -587,6 +595,8 @@
   @Override
   public TestResult run()
   {
+    defaultScenario = false;
+
     try
     {
       return super.run();
@@ -604,6 +614,8 @@
   @Override
   public void runBare() throws Throwable
   {
+    defaultScenario = false;
+
     try
     {
       super.runBare();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
index 89cf325..76fe0ba 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
@@ -18,6 +18,7 @@
 import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo;
 import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.common.util.CountedTimeProvider;
 import org.eclipse.emf.cdo.internal.common.revision.NOOPRevisionCache;
 import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionConfigurationImpl;
 import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionImpl;
@@ -105,6 +106,8 @@
 {
   public static final String PROP_TEST_REPOSITORY = "test.repository";
 
+  public static final String PROP_TEST_TIME_PROVIDER = "test.repository.TimeProvider";
+
   public static final String PROP_TEST_REVISION_MANAGER = "test.repository.RevisionManager";
 
   public static final String PROP_TEST_UNIT_MANAGER = "test.repository.UnitManager";
@@ -465,7 +468,8 @@
       }
     });
 
-    if (enableServerBrowser || Boolean.TRUE.equals(getTestProperty(PROP_TEST_ENABLE_SERVER_BROWSER)))
+    if (enableServerBrowser || Boolean.TRUE.equals(getTestProperty(PROP_TEST_ENABLE_SERVER_BROWSER))
+        || getCurrentTest().hasDefaultScenario())
     {
       serverBrowser = new CDOServerBrowser(repositories);
       serverBrowser.activate();
@@ -602,6 +606,11 @@
     Map<String, String> repoProps = getRepositoryProperties();
     InternalRepository repository = (InternalRepository)CDOServerUtil.createRepository(name, store, repoProps);
 
+    if (hasAnnotation(CountedTime.class))
+    {
+      repository.setTimeProvider(new CountedTimeProvider());
+    }
+
     InternalCDORevisionManager revisionManager = getTestRevisionManager();
     if (revisionManager == null)
     {
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/Scenario.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/Scenario.java
index 2e61658..0e99c07 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/Scenario.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/Scenario.java
@@ -17,6 +17,7 @@
 import org.eclipse.emf.cdo.tests.config.IScenario;
 import org.eclipse.emf.cdo.tests.config.ISessionConfig;
 
+import org.eclipse.net4j.util.CheckUtil;
 import org.eclipse.net4j.util.WrappedException;
 import org.eclipse.net4j.util.collection.CaseInsensitiveStringSet;
 import org.eclipse.net4j.util.io.IOUtil;
@@ -136,6 +137,11 @@
     Set<String> capabilities = new CaseInsensitiveStringSet();
     capabilities.add(IConfig.CAPABILITY_ALL);
 
+    if (CheckUtil.SANITIZE_TIMEOUT)
+    {
+      capabilities.add(IConfig.CAPABILITY_SANITIZE_TIMEOUT);
+    }
+
     repositoryConfig.initCapabilities(capabilities);
     sessionConfig.initCapabilities(capabilities);
     modelConfig.initCapabilities(capabilities);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
index 21ba71b..1307f40 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
@@ -28,7 +28,7 @@
 import java.util.Set;
 
 /**
- * A scalable {@link EContentAdapter content adapter} that uses CDO mechansims to attach itself to {@link CDOObject
+ * A scalable {@link EContentAdapter content adapter} that uses CDO mechanisms to attach itself to {@link CDOObject
  * objects} when they are lazily loaded.
  *
  * @author Victor Roldan Betancort
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOUtil.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOUtil.java
index c6ec39b..2003a0e 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOUtil.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOUtil.java
@@ -616,6 +616,14 @@
   }
 
   /**
+   * @since 4.6
+   */
+  public static CDOBranch createBranch(CDOBranchPoint base, String name)
+  {
+    return base.getBranch().createBranch(name, base.getTimeStamp());
+  }
+
+  /**
    * @since 4.3
    */
   public static <T extends EObject> EList<T> filterReadables(Collection<T> collection)
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
index ae2b458..ba43d6e 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
@@ -127,6 +127,7 @@
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.spi.cdo.CDOPermissionUpdater;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult;
 import org.eclipse.emf.spi.cdo.InternalCDOObject;
 import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager;
@@ -1181,7 +1182,8 @@
     CDORevisionAvailabilityInfo targetInfo = createRevisionAvailabilityInfo2(target);
     CDORevisionAvailabilityInfo sourceInfo = createRevisionAvailabilityInfo2(source);
 
-    Set<CDOID> ids = sessionProtocol.loadMergeData(targetInfo, sourceInfo, null, null);
+    MergeDataResult result = sessionProtocol.loadMergeData2(targetInfo, sourceInfo, null, null);
+    Set<CDOID> ids = result.getTargetIDs();
 
     cacheRevisions2(targetInfo);
     cacheRevisions2(sourceInfo);
@@ -1233,7 +1235,7 @@
     CDORevisionAvailabilityInfo targetBaseInfo = createRevisionAvailabilityInfo2(targetBase);
     CDORevisionAvailabilityInfo sourceBaseInfo = sameBase && !auto ? null : createRevisionAvailabilityInfo2(sourceBase);
 
-    Set<CDOID> ids = sessionProtocol.loadMergeData(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo);
+    MergeDataResult result = sessionProtocol.loadMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo);
 
     if (auto)
     {
@@ -1259,12 +1261,12 @@
     CDOChangeSet sourceChanges = null;
     if (computeChangeSets)
     {
-      targetChanges = createChangeSet(ids, targetBaseInfo, targetInfo);
-      sourceChanges = createChangeSet(ids, sourceBaseInfo, sourceInfo);
+      targetChanges = createChangeSet(result.getTargetIDs(), targetBaseInfo, targetInfo);
+      sourceChanges = createChangeSet(result.getSourceIDs(), sourceBaseInfo, sourceInfo);
     }
 
-    return new MergeData(target, source, sourceBase, targetBase, targetInfo, sourceInfo, sourceBaseInfo, targetBaseInfo,
-        ids, targetChanges, sourceChanges);
+    return new MergeData(target, targetInfo, targetBase, targetBaseInfo, result.getTargetIDs(), targetChanges, source,
+        sourceInfo, sourceBase, sourceBaseInfo, result.getSourceIDs(), sourceChanges, result.getResultBase());
   }
 
   @Deprecated
@@ -1275,11 +1277,11 @@
 
   private CDORevisionAvailabilityInfo createRevisionAvailabilityInfo2(CDOBranchPoint branchPoint)
   {
-    CDORevisionAvailabilityInfo info = new CDORevisionAvailabilityInfo(branchPoint);
+    InternalCDORevisionManager revisionManager = getRevisionManager();
+    CDORevisionAvailabilityInfo info = new CDORevisionAvailabilityInfo(branchPoint, revisionManager);
 
     if (branchPoint != CDOBranchUtil.AUTO_BRANCH_POINT)
     {
-      InternalCDORevisionManager revisionManager = getRevisionManager();
       InternalCDORevisionCache cache = revisionManager.getCache();
 
       List<CDORevision> revisions = cache.getRevisions(branchPoint);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
index 9db1623..10a8834 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
@@ -994,15 +994,22 @@
     }
   }
 
+  @Deprecated
   public Set<CDOID> loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
       CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
   {
+    throw new UnsupportedOperationException();
+  }
+
+  public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
+  {
     int attempt = 0;
     for (;;)
     {
       try
       {
-        return delegate.loadMergeData(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo);
+        return delegate.loadMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo);
       }
       catch (Exception ex)
       {
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java
index 9763292..a018653 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java
@@ -54,6 +54,7 @@
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
 import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
 import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
 import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
@@ -77,7 +78,6 @@
 import org.eclipse.emf.cdo.session.CDOSession;
 import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
 import org.eclipse.emf.cdo.spi.common.commit.CDOCommitInfoUtil;
-import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo;
 import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
 import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOClassInfo;
@@ -89,6 +89,7 @@
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider;
 import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision;
 import org.eclipse.emf.cdo.transaction.CDOCommitContext;
 import org.eclipse.emf.cdo.transaction.CDOConflictResolver;
@@ -609,12 +610,22 @@
           return null;
         }
 
-        CDORevisionAvailabilityInfo targetBaseInfo = mergeData.getTargetBaseInfo();
-        CDORevisionAvailabilityInfo targetInfo = mergeData.getTargetInfo();
-        ApplyChangeSetResult changeSet = applyChangeSet(result, targetBaseInfo, targetInfo, source, false);
+        CDORevisionProvider targetProvider = mergeData.getTargetInfo();
+        CDORevisionProvider resultBaseProvider;
 
+        CDORevisionManager revisionManager = session.getRevisionManager();
+        CDOBranchPoint resultBase = mergeData.getResultBase();
+        if (resultBase != null)
+        {
+          resultBaseProvider = new ManagedRevisionProvider(revisionManager, resultBase);
+        }
+        else
+        {
+          resultBaseProvider = mergeData.getTargetBaseInfo();
+        }
+
+        ApplyChangeSetResult changeSet = applyChangeSet(result, resultBaseProvider, targetProvider, source, false);
         commitMergeSource = source;
-
         return changeSet.getChangeSetData();
       }
       finally
@@ -636,7 +647,7 @@
     throw new UnsupportedOperationException();
   }
 
-  public ApplyChangeSetResult applyChangeSet(CDOChangeSetData changeSetData, CDORevisionProvider targetBaseProvider,
+  public ApplyChangeSetResult applyChangeSet(CDOChangeSetData changeSetData, CDORevisionProvider resultBaseProvider,
       CDORevisionProvider targetProvider, CDOBranchPoint source, boolean keepVersions) throws ChangeSetOutdatedException
   {
     synchronized (getViewMonitor())
@@ -665,7 +676,7 @@
 
         // Changed objects
         Map<CDOID, InternalCDORevision> oldRevisions = applyChangedObjects(changeSetData.getChangedObjects(),
-            targetBaseProvider, targetProvider, keepVersions, resultData.getChangedObjects());
+            resultBaseProvider, targetProvider, keepVersions, resultData.getChangedObjects());
 
         // Delta notifications
         Collection<CDORevisionDelta> notificationDeltas = lastSavepoint.getRevisionDeltas2().values();
@@ -782,7 +793,7 @@
   }
 
   private Map<CDOID, InternalCDORevision> applyChangedObjects(List<CDORevisionKey> changedObjects,
-      CDORevisionProvider targetBaseProvider, CDORevisionProvider targetProvider, boolean keepVersions,
+      CDORevisionProvider resultBaseProvider, CDORevisionProvider targetProvider, boolean keepVersions,
       List<CDORevisionKey> result) throws ChangeSetOutdatedException
   {
     Map<CDOID, InternalCDORevision> oldRevisions = CDOIDUtil.createMap();
@@ -793,10 +804,10 @@
 
     for (CDORevisionKey key : changedObjects)
     {
-      InternalCDORevisionDelta targetBaseGoalDelta = (InternalCDORevisionDelta)key;
-      targetBaseGoalDelta.setTarget(null);
-      CDOID id = targetBaseGoalDelta.getID();
-      InternalCDORevision targetBaseRevision = (InternalCDORevision)targetBaseProvider.getRevision(id);
+      InternalCDORevisionDelta resultBaseGoalDelta = (InternalCDORevisionDelta)key;
+      resultBaseGoalDelta.setTarget(null);
+      CDOID id = resultBaseGoalDelta.getID();
+      InternalCDORevision resultBaseRevision = (InternalCDORevision)resultBaseProvider.getRevision(id);
 
       InternalCDOObject object = getObject(id);
       boolean revisionChanged = false;
@@ -811,7 +822,7 @@
 
       oldRevisions.put(id, targetRevision);
 
-      InternalCDORevision goalRevision = targetBaseRevision.copy();
+      InternalCDORevision goalRevision = resultBaseRevision.copy();
       goalRevision.setBranchPoint(this);
       if (!keepVersions)
       {
@@ -819,14 +830,14 @@
       }
 
       goalRevision.setRevised(UNSPECIFIED_DATE);
-      targetBaseGoalDelta.applyTo(goalRevision);
+      resultBaseGoalDelta.applyTo(goalRevision);
 
       InternalCDORevisionDelta targetGoalDelta = goalRevision.compare(targetRevision);
       targetGoalDelta.setTarget(null);
 
       if (!targetGoalDelta.isEmpty())
       {
-        if (keepVersions && targetGoalDelta.getVersion() != targetBaseRevision.getVersion())
+        if (keepVersions && targetGoalDelta.getVersion() != resultBaseRevision.getVersion())
         {
           throw new ChangeSetOutdatedException();
         }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java
index 235029d..cb50297 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java
@@ -254,6 +254,7 @@
             currentCDOSavePoint.getDirtyObjects().remove(id);
             currentCDOSavePoint = currentCDOSavePoint.getPreviousSavepoint();
           }
+
           object.cdoInternalSetState(CDOState.CLEAN);
         }
         else
@@ -266,10 +267,9 @@
             currentCDOSavePoint.getDirtyObjects().put(id, object);
             currentCDOSavePoint = currentCDOSavePoint.getPreviousSavepoint();
           }
+
           object.cdoInternalSetState(CDOState.DIRTY);
-
           cleanRevisions.put(object, newCleanRevision);
-
           updateObjects(newCleanRevision, newLocalDelta, detachedObjectsUpdater);
         }
         object.cdoInternalPostLoad();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
index 00f87a7..ec22fb4 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
@@ -295,11 +295,19 @@
 
   /**
    * @since 4.0
+   * @deprecated As of 4.6 use {@link #loadMergeData2(CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo)}.
    */
+  @Deprecated
   public Set<CDOID> loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
       CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo);
 
   /**
+   * @since 4.6
+   */
+  public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+      CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo);
+
+  /**
    * @since 4.1
    * @deprecated Not called anymore. Use {@link #getLockStates(int, Collection, int)} instead.
    */
@@ -358,7 +366,7 @@
    * @since 3.0
    * @noinstantiate This class is not intended to be instantiated by clients.
    */
-  public final class OpenSessionResult extends PlatformObject implements CDOCommonRepository
+  public static final class OpenSessionResult extends PlatformObject implements CDOCommonRepository
   {
     private int sessionID;
 
@@ -855,7 +863,7 @@
    *
    * @author Eike Stepper
    */
-  public final class RepositoryTimeResult
+  public static final class RepositoryTimeResult
   {
     private long requested;
 
@@ -932,12 +940,49 @@
   }
 
   /**
+   * @author Eike Stepper
+   * @since 4.6
+   */
+  public static final class MergeDataResult
+  {
+    private final Set<CDOID> targetIDs = new HashSet<CDOID>();
+
+    private final Set<CDOID> sourceIDs = new HashSet<CDOID>();
+
+    private CDOBranchPoint resultBase;
+
+    public MergeDataResult()
+    {
+    }
+
+    public Set<CDOID> getTargetIDs()
+    {
+      return targetIDs;
+    }
+
+    public Set<CDOID> getSourceIDs()
+    {
+      return sourceIDs;
+    }
+
+    public CDOBranchPoint getResultBase()
+    {
+      return resultBase;
+    }
+
+    public void setResultBase(CDOBranchPoint resultBase)
+    {
+      this.resultBase = resultBase;
+    }
+  }
+
+  /**
    * If the meaning of this type isn't clear, there really should be more of a description here...
    *
    * @author Eike Stepper
    * @since 3.0
    */
-  public final class CommitTransactionResult implements CDOBranchPoint
+  public static final class CommitTransactionResult implements CDOBranchPoint
   {
     private CDOIDProvider idProvider;
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/DefaultCDOMerger.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/DefaultCDOMerger.java
index 479e4b9..773d3bc 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/DefaultCDOMerger.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/DefaultCDOMerger.java
@@ -802,11 +802,17 @@
           {
             CDOAddFeatureDelta addChange = (CDOAddFeatureDelta)change;
 
+            int index = addChange.getIndex();
+            if (index > list.size())
+            {
+              index = list.size();
+            }
+
             Element element = new Element(-1);
             element.set(side, addChange);
             allElements.put(addChange, element);
 
-            list.add(addChange.getIndex(), element);
+            list.add(index, element);
             rememberAddition(addChange.getValue(), element, additions);
             break;
           }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
index e4a678b..fb1b868 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
@@ -43,6 +43,7 @@
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -479,42 +480,65 @@
   {
     private final CDOBranchPoint target;
 
-    private final CDOBranchPoint source;
-
-    private final CDOBranchPoint sourceBase;
+    private final CDORevisionAvailabilityInfo targetInfo;
 
     private final CDOBranchPoint targetBase;
 
-    private final CDORevisionAvailabilityInfo targetInfo;
-
-    private final CDORevisionAvailabilityInfo sourceInfo;
-
-    private final CDORevisionAvailabilityInfo sourceBaseInfo;
-
     private final CDORevisionAvailabilityInfo targetBaseInfo;
 
-    private final Set<CDOID> ids;
+    private final Set<CDOID> targetIDs;
 
     private final CDOChangeSet targetChanges;
 
+    private final CDOBranchPoint source;
+
+    private final CDORevisionAvailabilityInfo sourceInfo;
+
+    private final CDOBranchPoint sourceBase;
+
+    private final CDORevisionAvailabilityInfo sourceBaseInfo;
+
+    private final Set<CDOID> sourceIDs;
+
     private final CDOChangeSet sourceChanges;
 
+    private final CDOBranchPoint resultBase;
+
+    /**
+     * @deprecated As of 4.6 use {@link #MergeData(CDOBranchPoint, CDORevisionAvailabilityInfo, CDOBranchPoint, CDORevisionAvailabilityInfo, Set, CDOChangeSet, CDOBranchPoint, CDORevisionAvailabilityInfo, CDOBranchPoint, CDORevisionAvailabilityInfo, Set, CDOChangeSet, CDOBranchPoint)}.
+     */
+    @Deprecated
     public MergeData(CDOBranchPoint target, CDOBranchPoint source, CDOBranchPoint sourceBase, CDOBranchPoint targetBase,
         CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
         CDORevisionAvailabilityInfo sourceBaseInfo, CDORevisionAvailabilityInfo targetBaseInfo, Set<CDOID> ids,
         CDOChangeSet targetChanges, CDOChangeSet sourceChanges)
     {
+      this(target, targetInfo, targetBase, targetBaseInfo, ids, targetChanges, source, sourceInfo, sourceBase,
+          sourceBaseInfo, ids, sourceChanges, null);
+    }
+
+    /**
+     * @since 4.6
+     */
+    public MergeData(CDOBranchPoint target, CDORevisionAvailabilityInfo targetInfo, CDOBranchPoint targetBase,
+        CDORevisionAvailabilityInfo targetBaseInfo, Set<CDOID> targetIDs, CDOChangeSet targetChanges,
+        CDOBranchPoint source, CDORevisionAvailabilityInfo sourceInfo, CDOBranchPoint sourceBase,
+        CDORevisionAvailabilityInfo sourceBaseInfo, Set<CDOID> sourceIDs, CDOChangeSet sourceChanges,
+        CDOBranchPoint resultBase)
+    {
       this.target = target;
-      this.source = source;
-      this.sourceBase = sourceBase;
-      this.targetBase = targetBase;
       this.targetInfo = targetInfo;
-      this.sourceInfo = sourceInfo;
-      this.sourceBaseInfo = sourceBaseInfo;
+      this.targetBase = targetBase;
       this.targetBaseInfo = targetBaseInfo;
-      this.ids = ids;
+      this.targetIDs = targetIDs;
       this.targetChanges = targetChanges;
+      this.source = source;
+      this.sourceInfo = sourceInfo;
+      this.sourceBase = sourceBase;
+      this.sourceBaseInfo = sourceBaseInfo;
+      this.sourceIDs = sourceIDs;
       this.sourceChanges = sourceChanges;
+      this.resultBase = resultBase;
     }
 
     public CDOBranchPoint getTarget()
@@ -522,14 +546,9 @@
       return target;
     }
 
-    public CDOBranchPoint getSource()
+    public CDORevisionAvailabilityInfo getTargetInfo()
     {
-      return source;
-    }
-
-    public CDOBranchPoint getSourceBase()
-    {
-      return sourceBase;
+      return targetInfo;
     }
 
     /**
@@ -540,9 +559,30 @@
       return targetBase;
     }
 
-    public CDORevisionAvailabilityInfo getTargetInfo()
+    /**
+     * @since 4.6
+     */
+    public CDORevisionAvailabilityInfo getTargetBaseInfo()
     {
-      return targetInfo;
+      return targetBaseInfo;
+    }
+
+    /**
+     * @since 4.6
+     */
+    public Set<CDOID> getTargetIDs()
+    {
+      return targetIDs;
+    }
+
+    public CDOChangeSet getTargetChanges()
+    {
+      return targetChanges;
+    }
+
+    public CDOBranchPoint getSource()
+    {
+      return source;
     }
 
     public CDORevisionAvailabilityInfo getSourceInfo()
@@ -550,6 +590,11 @@
       return sourceInfo;
     }
 
+    public CDOBranchPoint getSourceBase()
+    {
+      return sourceBase;
+    }
+
     /**
      * @since 4.6
      */
@@ -561,19 +606,9 @@
     /**
      * @since 4.6
      */
-    public CDORevisionAvailabilityInfo getTargetBaseInfo()
+    public Set<CDOID> getSourceIDs()
     {
-      return targetBaseInfo;
-    }
-
-    public Set<CDOID> getIDs()
-    {
-      return ids;
-    }
-
-    public CDOChangeSet getTargetChanges()
-    {
-      return targetChanges;
+      return sourceIDs;
     }
 
     public CDOChangeSet getSourceChanges()
@@ -582,6 +617,27 @@
     }
 
     /**
+     * @since 4.6
+     */
+    public CDOBranchPoint getResultBase()
+    {
+      return resultBase;
+    }
+
+    public Set<CDOID> getIDs()
+    {
+      if (targetIDs == sourceIDs)
+      {
+        return targetIDs;
+      }
+
+      Set<CDOID> ids = new HashSet<CDOID>();
+      ids.addAll(targetIDs);
+      ids.addAll(sourceIDs);
+      return ids;
+    }
+
+    /**
      * @deprecated As of 4.6 use {@link #getTargetBase()}.
      */
     @Deprecated
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/bundle/AbstractPlatform.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/bundle/AbstractPlatform.java
index 3faa47e..df23464 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/bundle/AbstractPlatform.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/bundle/AbstractPlatform.java
@@ -81,7 +81,7 @@
 
   protected AbstractPlatform()
   {
-    debugging = Boolean.parseBoolean(getProperty("debug", "false")); //$NON-NLS-1$ //$NON-NLS-2$
+    debugging = isProperty("debug"); //$NON-NLS-1$
   }
 
   public synchronized OMBundle bundle(String bundleID, Class<?> accessor)
@@ -340,6 +340,22 @@
     return System.getProperty(key, defaultValue);
   }
 
+  public boolean isProperty(String key)
+  {
+    return isProperty(key, false);
+  }
+
+  public boolean isProperty(String key, boolean defaultValue)
+  {
+    String property = getProperty(key);
+    if (property == null)
+    {
+      return defaultValue;
+    }
+
+    return Boolean.parseBoolean(property);
+  }
+
   protected abstract OMBundle createBundle(String bundleID, Class<?> accessor);
 
   protected abstract String getDebugOption(String bundleID, String option);
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/CheckUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/CheckUtil.java
index a93b581..9c0e8b5 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/CheckUtil.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/CheckUtil.java
@@ -11,6 +11,10 @@
 package org.eclipse.net4j.util;
 
 import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.om.OMPlatform;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
 
 /**
  * Provides static methods that check object states and invocation arguments.
@@ -20,6 +24,19 @@
  */
 public final class CheckUtil
 {
+  /**
+   * @since 3.7
+   */
+  public static final boolean HAS_DEBUGGER_ATTACHED = hasDebuggerAttached();
+
+  /**
+   * @since 3.7
+   */
+  public static final boolean SANITIZE_TIMEOUT = OMPlatform.INSTANCE
+      .isProperty("org.eclipse.net4j.util.CheckUtil.sanitizeTimeout", HAS_DEBUGGER_ATTACHED);
+
+  private static final long ONE_DAY = 1000 * 60 * 60 * 24;
+
   private static int counter;
 
   private CheckUtil()
@@ -81,4 +98,38 @@
   {
     IOUtil.OUT().println(message + --counter);
   }
+
+  /**
+   *
+   * @since 3.7
+   */
+  public static long sanitizeTimeout(long timeout)
+  {
+    if (SANITIZE_TIMEOUT)
+    {
+      timeout = Math.max(timeout, ONE_DAY);
+    }
+
+    return timeout;
+  }
+
+  private static boolean hasDebuggerAttached()
+  {
+    try
+    {
+      String property = OMPlatform.INSTANCE.getProperty("org.eclipse.net4j.util.CheckUtil.hasDebuggerAttached");
+      if (property != null)
+      {
+        return Boolean.parseBoolean(property);
+      }
+
+      RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+      String jvmArguments = runtimeMXBean.getInputArguments().toString();
+      return jvmArguments.contains("-agentlib:jdwp");
+    }
+    catch (Throwable ex)
+    {
+      return false;
+    }
+  }
 }
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Timeouter.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Timeouter.java
index 0309f9d..a63a54e 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Timeouter.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Timeouter.java
@@ -11,6 +11,7 @@
 package org.eclipse.net4j.util.concurrent;
 
 import org.eclipse.net4j.internal.util.bundle.OM;
+import org.eclipse.net4j.util.CheckUtil;
 
 import java.util.Timer;
 import java.util.TimerTask;
@@ -32,8 +33,8 @@
   public Timeouter(Timer timer, long timeout)
   {
     this.timer = timer;
-    this.timeout = timeout;
 
+    setTimeout(timeout);
     touch();
     scheduleTimeout();
   }
@@ -45,7 +46,7 @@
 
   public void setTimeout(long timeout)
   {
-    this.timeout = timeout;
+    this.timeout = CheckUtil.sanitizeTimeout(timeout);
   }
 
   public void touch()
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/OMPlatform.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/OMPlatform.java
index 0aa004d..31ed7c3 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/OMPlatform.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/OMPlatform.java
@@ -86,6 +86,16 @@
   public String getProperty(String key, String defaultValue);
 
   /**
+   * @since 3.7
+   */
+  public boolean isProperty(String key);
+
+  /**
+   * @since 3.7
+   */
+  public boolean isProperty(String key, boolean defaultValue);
+
+  /**
    * @since 3.2
    */
   public String[] getCommandLineArgs() throws IllegalStateException;