Redesign savepoints
diff --git a/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters b/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
index 6f5fed2..01da2d6 100644
--- a/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
+++ b/plugins/org.eclipse.emf.cdo.common/.settings/.api_filters
@@ -149,6 +149,20 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java" type="org.eclipse.emf.cdo.common.revision.CDORevisionUtil">
+        <filter id="1143996420">
+            <message_arguments>
+                <message_argument value="areValuesEqual(Object, Object)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/cdo/common/revision/delta/CDOOriginSizeProvider.java" type="org.eclipse.emf.cdo.common.revision.delta.CDOOriginSizeProvider$Caching">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="Caching"/>
+            </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>
@@ -177,4 +191,11 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/emf/cdo/spi/common/revision/InternalCDOFeatureDelta.java" type="org.eclipse.emf.cdo.spi.common.revision.InternalCDOFeatureDelta$WithIndex">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getIndex()"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java
index eb43888..9103791 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java
@@ -68,6 +68,45 @@
   {
   }
 
+  private static Object convertEObject(Object value)
+  {
+    CDOID id = CDOIDUtil.getCDOID(value);
+    if (id != null)
+    {
+      return id;
+    }
+
+    return value;
+  }
+
+  public static boolean areValuesEqual(Object value1, Object value2)
+  {
+    Object v1 = convertEObject(value1);
+    Object v2 = convertEObject(value2);
+
+    if (v1 == null)
+    {
+      return v2 == null;
+    }
+
+    if (v2 == null)
+    {
+      return false;
+    }
+
+    if (v1 == v2)
+    {
+      return true;
+    }
+
+    if (v1 instanceof CDOID)
+    {
+      return false;
+    }
+
+    return v1.equals(v2);
+  }
+
   /**
    * Creates and returns a new memory sensitive revision cache.
    *
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/delta/CDOOriginSizeProvider.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/delta/CDOOriginSizeProvider.java
index 3021906..9d37ab7 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/delta/CDOOriginSizeProvider.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/delta/CDOOriginSizeProvider.java
@@ -10,6 +10,8 @@
  */
 package org.eclipse.emf.cdo.common.revision.delta;
 
+import org.eclipse.emf.cdo.common.revision.CDOList;
+
 /**
  * If the meaning of this type isn't clear, there really should be more of a description here...
  *
@@ -19,4 +21,27 @@
 public interface CDOOriginSizeProvider
 {
   public int getOriginSize();
+
+  /**
+   * @author Eike Stepper
+   */
+  public static abstract class Caching implements CDOOriginSizeProvider
+  {
+    private static final int UNKNOWN = -1;
+
+    private int originSize = UNKNOWN;
+
+    public int getOriginSize()
+    {
+      if (originSize == UNKNOWN)
+      {
+        CDOList list = getList();
+        originSize = list.size();
+      }
+
+      return originSize;
+    }
+
+    protected abstract CDOList getList();
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOChangeSetDataImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOChangeSetDataImpl.java
index 067021d..251861d 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOChangeSetDataImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/commit/CDOChangeSetDataImpl.java
@@ -168,7 +168,7 @@
         InternalCDORevisionDelta newDelta = (InternalCDORevisionDelta)oldDelta;
         for (CDOFeatureDelta featureDelta : delta.getFeatureDeltas())
         {
-          newDelta.addFeatureDelta(featureDelta, null);
+          newDelta.mergeFeatureDelta(featureDelta, null);
         }
 
         return;
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOAddFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOAddFeatureDeltaImpl.java
index 20f7f27..27d89cf 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOAddFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOAddFeatureDeltaImpl.java
@@ -27,7 +27,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOAddFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOAddFeatureDelta,
+public final class CDOAddFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOAddFeatureDelta,
     ListIndexAffecting, ListTargetAdding
 {
   public CDOAddFeatureDeltaImpl(EStructuralFeature feature, int index, Object value)
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOClearFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOClearFeatureDeltaImpl.java
index 363082a..3cc29f2 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOClearFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOClearFeatureDeltaImpl.java
@@ -27,7 +27,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOClearFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOClearFeatureDelta
+public final class CDOClearFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOClearFeatureDelta
 {
   public CDOClearFeatureDeltaImpl(EStructuralFeature feature)
   {
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOContainerFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOContainerFeatureDeltaImpl.java
index a675ff5..2d5ea36 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOContainerFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOContainerFeatureDeltaImpl.java
@@ -34,7 +34,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOContainerFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOContainerFeatureDelta
+public final class CDOContainerFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOContainerFeatureDelta
 {
   private CDOID newResourceID;
 
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOListFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOListFeatureDeltaImpl.java
index 54a2e0e..d4fde12 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOListFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOListFeatureDeltaImpl.java
@@ -44,7 +44,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOListFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOListFeatureDelta
+public final class CDOListFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOListFeatureDelta
 {
   private final int originSize;
 
@@ -398,7 +398,7 @@
     return true;
   }
 
-  public void add(CDOFeatureDelta featureDelta)
+  public void mergeFeatureDelta(CDOFeatureDelta featureDelta)
   {
     // Only adds the feature delta to the list if required.
     if (cleanupWithNewDelta(featureDelta))
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOMoveFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOMoveFeatureDeltaImpl.java
index dbbca57..e795154 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOMoveFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOMoveFeatureDeltaImpl.java
@@ -31,8 +31,8 @@
 /**
  * @author Simon McDuff
  */
-public class CDOMoveFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOMoveFeatureDelta, ListIndexAffecting,
-    WithIndex
+public final class CDOMoveFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOMoveFeatureDelta,
+    ListIndexAffecting, WithIndex
 {
   private int oldPosition;
 
@@ -40,12 +40,12 @@
 
   private Object value;
 
-  public CDOMoveFeatureDeltaImpl(EStructuralFeature feature, int newPosition, int oldPosition)
+  public CDOMoveFeatureDeltaImpl(EStructuralFeature feature, int newPosition, int oldPosition, Object value)
   {
     super(feature);
     this.newPosition = newPosition;
     this.oldPosition = oldPosition;
-    value = UNKNOWN_VALUE;
+    this.value = value;
   }
 
   public CDOMoveFeatureDeltaImpl(CDODataInput in, EClass eClass) throws IOException
@@ -74,6 +74,11 @@
     return oldPosition;
   }
 
+  public int getIndex()
+  {
+    return oldPosition;
+  }
+
   public Type getType()
   {
     return Type.MOVE;
@@ -101,9 +106,7 @@
 
   public CDOFeatureDelta copy()
   {
-    CDOFeatureDelta copy = new CDOMoveFeatureDeltaImpl(getFeature(), newPosition, oldPosition);
-    ((CDOMoveFeatureDeltaImpl)copy).setValue(getValue());
-    return copy;
+    return new CDOMoveFeatureDeltaImpl(getFeature(), newPosition, oldPosition, value);
   }
 
   public void apply(CDORevision revision)
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORemoveFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORemoveFeatureDeltaImpl.java
index e767520..65e67e6 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORemoveFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORemoveFeatureDeltaImpl.java
@@ -28,12 +28,12 @@
 /**
  * @author Simon McDuff
  */
-public class CDORemoveFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDORemoveFeatureDelta,
+public final class CDORemoveFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDORemoveFeatureDelta,
     ListIndexAffecting
 {
-  public CDORemoveFeatureDeltaImpl(EStructuralFeature feature, int index)
+  public CDORemoveFeatureDeltaImpl(EStructuralFeature feature, int index, Object value)
   {
-    super(feature, index, UNKNOWN_VALUE);
+    super(feature, index, value);
   }
 
   public CDORemoveFeatureDeltaImpl(CDODataInput in, EClass eClass) throws IOException
@@ -60,7 +60,7 @@
 
   public CDOFeatureDelta copy()
   {
-    CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(getFeature(), getIndex());
+    CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(getFeature(), getIndex(), getValue());
     delta.setValue(getValue());
     return delta;
   }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java
index 9fdb264..b93f98a 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java
@@ -14,7 +14,6 @@
 
 import org.eclipse.emf.cdo.common.branch.CDOBranch;
 import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 import org.eclipse.emf.cdo.common.id.CDOWithID;
 import org.eclipse.emf.cdo.common.protocol.CDODataInput;
 import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
@@ -26,10 +25,12 @@
 import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
 import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
 import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta.Type;
 import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
 import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
 import org.eclipse.emf.cdo.common.revision.delta.CDOOriginSizeProvider;
 import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
 import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
 import org.eclipse.emf.cdo.common.util.PartialCollectionLoadingNotSupportedException;
 import org.eclipse.emf.cdo.internal.common.revision.CDOListImpl;
@@ -95,7 +96,7 @@
       for (CDOFeatureDelta delta : revisionDelta.getFeatureDeltas())
       {
         CDOFeatureDelta copy = ((InternalCDOFeatureDelta)delta).copy();
-        addFeatureDelta(copy, null);
+        mergeFeatureDelta(copy, null);
       }
     }
   }
@@ -126,13 +127,13 @@
 
     CDOID dirtyResourceID = dirtyData.getResourceID();
     int dirtyContainingFeatureID = dirtyData.getContainingFeatureID();
-    if (!compareValue(originData.getContainerID(), dirtyContainerID)
-        || !compareValue(originData.getContainingFeatureID(), dirtyContainingFeatureID)
-        || !compareValue(originData.getResourceID(), dirtyResourceID))
+    if (!CDORevisionUtil.areValuesEqual(originData.getContainerID(), dirtyContainerID)
+        || !CDORevisionUtil.areValuesEqual(originData.getContainingFeatureID(), dirtyContainingFeatureID)
+        || !CDORevisionUtil.areValuesEqual(originData.getResourceID(), dirtyResourceID))
     {
       CDOFeatureDelta delta = new CDOContainerFeatureDeltaImpl(dirtyResourceID, dirtyContainerID,
           dirtyContainingFeatureID);
-      addFeatureDelta(delta, null);
+      mergeFeatureDelta(delta, null);
     }
   }
 
@@ -262,23 +263,31 @@
     throw new UnsupportedOperationException();
   }
 
+  @Deprecated
   public void addFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider)
   {
+    mergeFeatureDelta(delta, originSizeProvider);
+  }
+
+  public boolean mergeFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider)
+  {
     if (delta instanceof CDOListFeatureDelta)
     {
       CDOListFeatureDelta listDelta = (CDOListFeatureDelta)delta;
+
+      boolean lastDeltaResultsInNoChange = false;
       for (CDOFeatureDelta listChange : listDelta.getListChanges())
       {
-        addFeatureDelta(listChange, listDelta);
+        lastDeltaResultsInNoChange = mergeFeatureDelta(listChange, listDelta);
       }
+
+      return lastDeltaResultsInNoChange;
     }
-    else
-    {
-      addSingleFeatureDelta(delta, originSizeProvider);
-    }
+
+    return addSingleFeatureDelta(delta, originSizeProvider);
   }
 
-  private void addSingleFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider)
+  private boolean addSingleFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider)
   {
     EStructuralFeature feature = delta.getFeature();
     if (feature.isMany())
@@ -292,17 +301,64 @@
       }
 
       // Remove all previous changes
+      List<CDOFeatureDelta> listChanges = listDelta.getListChanges();
       if (delta instanceof CDOClearFeatureDelta || delta instanceof CDOUnsetFeatureDelta)
       {
-        listDelta.getListChanges().clear();
+        listChanges.clear();
       }
 
-      listDelta.add(delta);
+      listDelta.mergeFeatureDelta(delta); // Net result can be empty!
+
+      if (listChanges.isEmpty())
+      {
+        featureDeltas.remove(feature);
+        return true;
+      }
     }
     else
     {
-      featureDeltas.put(feature, delta);
+      CDOFeatureDelta old = featureDeltas.put(feature, delta);
+      if (old != null)
+      {
+        if (old.getType() == Type.SET && delta.getType() == Type.SET)
+        {
+          Object oldValue = ((CDOSetFeatureDelta)old).getOldValue();
+
+          CDOSetFeatureDeltaImpl newSetDelta = (CDOSetFeatureDeltaImpl)delta;
+          if (CDORevisionUtil.areValuesEqual(oldValue, newSetDelta.getValue()))
+          {
+            featureDeltas.remove(feature);
+            return true;
+          }
+
+          newSetDelta.setOldValue(oldValue);
+        }
+        else
+        {
+          // TODO Handle SET / UNSET combinations?
+        }
+      }
+      else
+      {
+        // CDOStoreImpl.set() should prevent this case!
+
+        // if (delta.getType() == Type.SET)
+        // {
+        // CDOSetFeatureDelta setDelta = (CDOSetFeatureDelta)delta;
+        // if (compareValue(setDelta.getOldValue(), setDelta.getValue()))
+        // {
+        // featureDeltas.remove(feature);
+        // return true;
+        // }
+        // }
+        // else
+        // {
+        // // TODO Handle UNSET?
+        // }
+      }
     }
+
+    return false;
   }
 
   public boolean adjustReferences(CDOReferenceAdjuster referenceAdjuster)
@@ -345,7 +401,7 @@
         final int originSize = originData.size(feature);
         if (originSize > 0 && dirtyData.size(feature) == 0)
         {
-          addFeatureDelta(new CDOClearFeatureDeltaImpl(feature), new CDOOriginSizeProvider()
+          mergeFeatureDelta(new CDOClearFeatureDeltaImpl(feature), new CDOOriginSizeProvider()
           {
             public int getOriginSize()
             {
@@ -381,9 +437,13 @@
             protected void createRemoveListChange(EList<?> oldList, EList<ListChange> listChanges, Object value,
                 int index)
             {
-              CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(feature, index);
-              // fix until ListDifferenceAnalyzer delivers the correct value (bug #308618).
-              delta.setValue(oldList.get(index));
+              int xxx;
+              // CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(feature, index);
+              // // fix until ListDifferenceAnalyzer delivers the correct value (bug 308618).
+              // delta.setValue(oldList.get(index));
+
+              // Valid since EMF 2.6 (bug 308618)
+              CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(feature, index, value);
               changes.add(delta);
               oldList.remove(index);
             }
@@ -392,9 +452,13 @@
             protected void createMoveListChange(EList<?> oldList, EList<ListChange> listChanges, Object value,
                 int index, int toIndex)
             {
-              CDOMoveFeatureDeltaImpl delta = new CDOMoveFeatureDeltaImpl(feature, toIndex, index);
-              // fix until ListDifferenceAnalyzer delivers the correct value (same problem as bug #308618).
-              delta.setValue(oldList.get(index));
+              int xxx;
+              // CDOMoveFeatureDeltaImpl delta = new CDOMoveFeatureDeltaImpl(feature, toIndex, index);
+              // // fix until ListDifferenceAnalyzer delivers the correct value (same problem as bug 308618).
+              // delta.setValue(oldList.get(index));
+
+              // Valid since EMF 2.6 (bug 308618)
+              CDOMoveFeatureDeltaImpl delta = new CDOMoveFeatureDeltaImpl(feature, toIndex, index, value);
               changes.add(delta);
               oldList.move(toIndex, index);
             }
@@ -402,7 +466,7 @@
             @Override
             protected boolean equal(Object originValue, Object dirtyValue)
             {
-              return compareValue(originValue, dirtyValue);
+              return CDORevisionUtil.areValuesEqual(originValue, dirtyValue);
             }
 
             private void checkNoProxies(EList<?> list, CDORevision revision)
@@ -434,62 +498,23 @@
       {
         Object originValue = originData.get(feature, 0);
         Object dirtyValue = dirtyData.get(feature, 0);
-        if (!compareValue(originValue, dirtyValue))
+        if (!CDORevisionUtil.areValuesEqual(originValue, dirtyValue))
         {
           if (dirtyValue == null)
           {
             CDOFeatureDelta delta = new CDOUnsetFeatureDeltaImpl(feature);
-            addFeatureDelta(delta, null);
+            mergeFeatureDelta(delta, null);
           }
           else
           {
             CDOFeatureDelta delta = new CDOSetFeatureDeltaImpl(feature, 0, dirtyValue, originValue);
-            addFeatureDelta(delta, null);
+            mergeFeatureDelta(delta, null);
           }
         }
       }
     }
   }
 
-  private boolean compareValue(Object originValue, Object dirtyValue)
-  {
-    Object origin = convertEObject(originValue);
-    Object dirty = convertEObject(dirtyValue);
-
-    if (origin == null)
-    {
-      return dirty == null;
-    }
-
-    if (dirty == null)
-    {
-      return false;
-    }
-
-    if (origin == dirty)
-    {
-      return true;
-    }
-
-    if (origin instanceof CDOID)
-    {
-      return false;
-    }
-
-    return origin.equals(dirty);
-  }
-
-  private Object convertEObject(Object value)
-  {
-    CDOID id = CDOIDUtil.getCDOID(value);
-    if (id != null)
-    {
-      return id;
-    }
-
-    return value;
-  }
-
   @Override
   public String toString()
   {
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOSetFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOSetFeatureDeltaImpl.java
index 5ac10c8..5660fcb 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOSetFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOSetFeatureDeltaImpl.java
@@ -28,7 +28,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOSetFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOSetFeatureDelta,
+public final class CDOSetFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOSetFeatureDelta,
     ListTargetAdding
 {
   private Object oldValue = CDOSetFeatureDelta.UNSPECIFIED;
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOUnsetFeatureDeltaImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOUnsetFeatureDeltaImpl.java
index 690e07d..9dbdb6e 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOUnsetFeatureDeltaImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOUnsetFeatureDeltaImpl.java
@@ -27,7 +27,7 @@
 /**
  * @author Simon McDuff
  */
-public class CDOUnsetFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOUnsetFeatureDelta
+public final class CDOUnsetFeatureDeltaImpl extends CDOFeatureDeltaImpl implements CDOUnsetFeatureDelta
 {
   public CDOUnsetFeatureDeltaImpl(EStructuralFeature feature)
   {
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/BaseCDORevision.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/BaseCDORevision.java
index e24c87c..d6d5349 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/BaseCDORevision.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/BaseCDORevision.java
@@ -56,6 +56,7 @@
 
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -591,7 +592,36 @@
 
   public void unset(EStructuralFeature feature)
   {
-    setValue(feature, null);
+    if (feature.isUnsettable())
+    {
+      setValue(feature, null);
+    }
+    else
+    {
+      if (feature.isMany())
+      {
+        Object value = getValue(feature);
+
+        @SuppressWarnings("unchecked")
+        List<Object> list = (List<Object>)value;
+        if (list != null)
+        {
+          list.clear();
+        }
+      }
+      else
+      {
+        Object defaultValue = null;
+
+        CDOType type = CDOModelUtil.getType(feature.getEType());
+        if (type != null)
+        {
+          defaultValue = type.convertToCDO(feature.getEType(), feature.getDefaultValue());
+        }
+
+        setValue(feature, defaultValue);
+      }
+    }
   }
 
   /**
@@ -655,6 +685,11 @@
 
   public Object getValue(EStructuralFeature feature)
   {
+    if (feature == CDOContainerFeatureDelta.CONTAINER_FEATURE)
+    {
+      return containerID;
+    }
+
     int featureIndex = getFeatureIndex(feature);
     return getValue(featureIndex);
   }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDOFeatureDelta.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDOFeatureDelta.java
index ad447f2..5a2a47c 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDOFeatureDelta.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDOFeatureDelta.java
@@ -30,6 +30,8 @@
    */
   public interface WithIndex
   {
+    public int getIndex();
+
     public void adjustAfterAddition(int index);
 
     public void adjustAfterRemoval(int index);
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDORevisionDelta.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDORevisionDelta.java
index 6cd5d56..e0392e4 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDORevisionDelta.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/InternalCDORevisionDelta.java
@@ -43,10 +43,20 @@
 
   /**
    * @since 4.2
+   * @deprecated As of 4.3 use {@link #mergeFeatureDelta(CDOFeatureDelta, CDOOriginSizeProvider)}.
    */
+  @Deprecated
   public void addFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider);
 
   /**
+   * Merges the given feature delta to this revision delta and returns <code>true</code> if the 
+   * merge result for the associated feature is empty, <code>false</code> otherwise.
+   * 
+   * @since 4.3
+   */
+  public boolean mergeFeatureDelta(CDOFeatureDelta delta, CDOOriginSizeProvider originSizeProvider);
+
+  /**
    * @since 3.0
    */
   public void setBranch(CDOBranch branch);
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java
index 9b3a13d..2dde669 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java
@@ -107,8 +107,9 @@
   @Override
   protected Object confirming(CDODataInput in) throws IOException
   {
+    Object accessValue = null;
     CDOType type = CDOModelUtil.getType(feature);
-    Object accessID = null;
+
     InternalCDOList list = (InternalCDOList)revision.getList(feature);
     for (int i = fromIndex; i <= toIndex; i++)
     {
@@ -116,10 +117,10 @@
       list.setWithoutFrozenCheck(i, value);
       if (i == accessIndex)
       {
-        accessID = value;
+        accessValue = value;
       }
     }
 
-    return accessID;
+    return accessValue;
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AbstractCDOTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AbstractCDOTest.java
index f37e8d2..e4b07a5 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AbstractCDOTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AbstractCDOTest.java
@@ -120,10 +120,9 @@
     }
   }
 
-  protected static void assertNotTransient(EObject eObject, CDOView view)
+  private static void assertNotTransient(EObject eObject, CDOView view)
   {
     CDOObject object = FSMUtil.adapt(eObject, view);
-    assertEquals(false, FSMUtil.isTransient(object));
     assertNotNull(object.cdoID());
     assertNotNull(object.cdoRevision());
     assertNotNull(object.cdoView());
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
index 9f77ee8..bca24ce 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
@@ -27,14 +27,14 @@
   protected void initConfigSuites(TestSuite parent)
   {
     addScenario(parent, MEM, JVM, NATIVE);
-    addScenario(parent, MEM_AUDITS, JVM, NATIVE);
-    addScenario(parent, MEM_BRANCHES, JVM, NATIVE);
-    addScenario(parent, MEM_BRANCHES_UUIDS, JVM, NATIVE);
-
-    addScenario(parent, MEM, JVM, LEGACY);
-    addScenario(parent, MEM_AUDITS, JVM, LEGACY);
-    addScenario(parent, MEM_BRANCHES, JVM, LEGACY);
-
-    addScenario(parent, MEM_BRANCHES, TCP, NATIVE);
+    // addScenario(parent, MEM_AUDITS, JVM, NATIVE);
+    // addScenario(parent, MEM_BRANCHES, JVM, NATIVE);
+    // addScenario(parent, MEM_BRANCHES_UUIDS, JVM, NATIVE);
+    //
+    // addScenario(parent, MEM, JVM, LEGACY);
+    // addScenario(parent, MEM_AUDITS, JVM, LEGACY);
+    // addScenario(parent, MEM_BRANCHES, JVM, LEGACY);
+    //
+    // addScenario(parent, MEM_BRANCHES, TCP, NATIVE);
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChunkingTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChunkingTest.java
index e0a4465..3a6a532 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChunkingTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChunkingTest.java
@@ -12,10 +12,13 @@
 
 import org.eclipse.emf.cdo.eresource.CDOResource;
 import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.tests.model1.Category;
+import org.eclipse.emf.cdo.tests.model1.Company;
 import org.eclipse.emf.cdo.tests.model1.Customer;
 import org.eclipse.emf.cdo.tests.model1.SalesOrder;
 import org.eclipse.emf.cdo.tests.model5.GenListOfInt;
 import org.eclipse.emf.cdo.tests.model5.Model5Factory;
+import org.eclipse.emf.cdo.transaction.CDOSavepoint;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.util.CommitException;
@@ -319,6 +322,79 @@
     testListResult(0, 1, 2, 3, 4, 6, 7, 8);
   }
 
+  public void testPartiallyLoadedUseAfterAddition() throws CommitException
+  {
+    createInitialList();
+
+    CDOSession session = openSession();
+    session.options().setCollectionLoadingPolicy(CDOUtil.createCollectionLoadingPolicy(3, 1));
+    CDOTransaction tx = session.openTransaction();
+    CDOResource resource = tx.getResource(getResourcePath(RESOURCE_PATH));
+
+    GenListOfInt list = (GenListOfInt)resource.getContents().get(0);
+    EList<Integer> elements = list.getElements();
+
+    elements.add(5, 666);
+    tx.commit();
+
+    int lastElement = elements.get(8);
+    assertEquals(7, lastElement);
+  }
+
+  public void testSetTwice() throws CommitException
+  {
+    Company company = getModel1Factory().createCompany();
+    company.setName("FirstName");
+
+    CDOSession session = openSession();
+    CDOTransaction tx = session.openTransaction();
+    CDOSavepoint savepoint = tx.getLastSavepoint();
+
+    CDOResource resource = tx.createResource(getResourcePath(RESOURCE_PATH));
+    resource.getContents().add(company);
+    tx.commit();
+
+    company.setName("SecondName");
+    company.setName("FirstName");
+
+    System.out.println(savepoint);
+  }
+
+  public void testSetSame() throws CommitException
+  {
+    Company company = getModel1Factory().createCompany();
+    company.setName("FirstName");
+
+    CDOSession session = openSession();
+    CDOTransaction tx = session.openTransaction();
+    CDOSavepoint savepoint = tx.getLastSavepoint();
+
+    CDOResource resource = tx.createResource(getResourcePath(RESOURCE_PATH));
+    resource.getContents().add(company);
+    tx.commit();
+
+    company.setName("FirstName");
+    System.out.println(savepoint);
+  }
+
+  public void testAddRemove() throws CommitException
+  {
+    Company company = getModel1Factory().createCompany();
+    EList<Category> categories = company.getCategories();
+
+    CDOSession session = openSession();
+    CDOTransaction tx = session.openTransaction();
+    CDOSavepoint savepoint = tx.getLastSavepoint();
+
+    CDOResource resource = tx.createResource(getResourcePath(RESOURCE_PATH));
+    resource.getContents().add(company);
+    tx.commit();
+
+    categories.add(getModel1Factory().createCategory());
+    categories.remove(0);
+    System.out.println(savepoint);
+  }
+
   private void createInitialList() throws CommitException
   {
     GenListOfInt list = Model5Factory.eINSTANCE.createGenListOfInt();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CrossReferenceTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CrossReferenceTest.java
index 00ef7cd..aa3ed09 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CrossReferenceTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CrossReferenceTest.java
@@ -284,11 +284,15 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     transaction.commit();
 
-    company.getCustomers().remove(customer);
+    Resource external = new ResourceImpl(URI.createURI("external:1"));
+    transaction.getResourceSet().getResources().add(external);
+    external.getContents().add(customer); // Detach customer from company
+
+    // company.getCustomers().remove(customer); // Detach customer from company
 
     try
     {
@@ -315,7 +319,7 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     transaction.commit();
 
@@ -343,13 +347,13 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     transaction.commit();
 
     Resource externalResource = new ResourceImpl(URI.createFileURI("/x/y/z"));
     transaction.getResourceSet().getResources().add(externalResource);
-    externalResource.getContents().add(customer);
+    externalResource.getContents().add(customer); // Detach customer from company
 
     transaction.commit();
     CDORevisionData data = CDOUtil.getCDOObject(salesOrder).cdoRevision().data();
@@ -372,7 +376,7 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     transaction.commit();
 
@@ -409,7 +413,7 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     // DO NOT: transaction.commit();
 
@@ -438,7 +442,7 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     // DO NOT: transaction.commit();
 
@@ -473,7 +477,7 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/company/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(company);
     // DO NOT: transaction.commit();
 
@@ -509,14 +513,14 @@
 
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
-    CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
+    CDOResource resource = transaction.createResource(getResourcePath("resource"));
     resource.getContents().add(parent);
     transaction.commit();
     session.close();
 
     session = openSession();
     transaction = session.openTransaction();
-    resource = transaction.getResource(getResourcePath("/my/resource"));
+    resource = transaction.getResource(getResourcePath("resource"));
     EList<EObject> contents = resource.getContents();
     assertEquals(1, contents.size());
 
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/DetachTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/DetachTest.java
index a924697..c23e10b 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/DetachTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/DetachTest.java
@@ -47,18 +47,18 @@
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
 
-    Company c1 = getModel1Factory().createCompany();
-    c1.setName("Test");
-    resource.getContents().add(c1);
+    Company company = getModel1Factory().createCompany();
+    company.setName("Test");
+    resource.getContents().add(company);
 
-    URI uriC1 = EcoreUtil.getURI(c1);
-    assertEquals(c1, transaction.getResourceSet().getEObject(uriC1, false));
+    URI companyURI = EcoreUtil.getURI(company);
+    assertEquals(company, transaction.getResourceSet().getEObject(companyURI, false));
 
     resource.getContents().remove(0); // remove object by index
-    assertNull(transaction.getResourceSet().getEObject(uriC1, false));
+    assertNull(transaction.getResourceSet().getEObject(companyURI, false));
 
     transaction.commit();
-    assertNull(transaction.getResourceSet().getEObject(uriC1, false));
+    assertNull(transaction.getResourceSet().getEObject(companyURI, false));
     session.close();
   }
 
@@ -68,22 +68,22 @@
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
 
-    Company c1 = getModel1Factory().createCompany();
-    c1.setName("Test");
-    resource.getContents().add(c1);
+    Company company = getModel1Factory().createCompany();
+    company.setName("Test");
+    resource.getContents().add(company);
     transaction.commit(); // (2)
 
-    final URI uriC1 = EcoreUtil.getURI(c1);
-    final CDOID id = CDOUtil.getCDOObject(c1).cdoID();
-    assertEquals(c1, transaction.getResourceSet().getEObject(uriC1, false));
+    final URI companyURI = EcoreUtil.getURI(company);
+    final CDOID id = CDOUtil.getCDOObject(company).cdoID();
+    assertEquals(company, transaction.getResourceSet().getEObject(companyURI, false));
 
-    resource.getContents().remove(c1);
-    assertTransient(c1);
-    assertSame(c1, CDOUtil.getEObject(transaction.getObject(id)));
-    assertSame(c1, transaction.getResourceSet().getEObject(uriC1, false));
+    resource.getContents().remove(company);
+    assertTransient(company);
+    assertSame(company, CDOUtil.getEObject(transaction.getObject(id)));
+    assertSame(company, transaction.getResourceSet().getEObject(companyURI, false));
 
     transaction.commit();
-    assertTransient(c1);
+    assertTransient(company);
 
     try
     {
@@ -96,7 +96,7 @@
       // SUCCESS
     }
 
-    assertNull(transaction.getResourceSet().getEObject(uriC1, false));
+    assertNull(transaction.getResourceSet().getEObject(companyURI, false));
     session.close();
   }
 
@@ -106,9 +106,9 @@
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
 
-    Company c1 = getModel1Factory().createCompany();
-    c1.setName("Test");
-    resource.getContents().add(c1);
+    Company company = getModel1Factory().createCompany();
+    company.setName("Test");
+    resource.getContents().add(company);
 
     savePointObjectDeletion(transaction, resource);
   }
@@ -119,12 +119,12 @@
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
 
-    Company c1 = getModel1Factory().createCompany();
-    c1.setName("Test");
-    resource.getContents().add(c1);
+    Company company = getModel1Factory().createCompany();
+    company.setName("Test");
+    resource.getContents().add(company);
 
-    URI uriC1 = EcoreUtil.getURI(c1);
-    assertEquals(c1, transaction.getResourceSet().getEObject(uriC1, false));
+    URI companyURI = EcoreUtil.getURI(company);
+    assertEquals(company, transaction.getResourceSet().getEObject(companyURI, false));
 
     transaction.commit();
     savePointObjectDeletion(transaction, resource);
@@ -132,52 +132,52 @@
 
   private void savePointObjectDeletion(CDOTransaction transaction, CDOResource resource)
   {
-    Company c1 = (Company)resource.getContents().get(0);
+    Company company = (Company)resource.getContents().get(0);
 
-    URI uriC1 = EcoreUtil.getURI(c1);
-    CDOObject companyCDOObject = CDOUtil.getCDOObject(c1);
+    URI companyURI = EcoreUtil.getURI(company);
+    CDOObject companyCDOObject = CDOUtil.getCDOObject(company);
     boolean isPersisted = !FSMUtil.isNew(companyCDOObject);
-    c1.setName("SIMON");
+    company.setName("SIMON");
 
     CDOUserSavepoint savepoint = transaction.setSavepoint();
     resource.getContents().remove(0); // remove object by index
-    assertTransient(c1);
+    assertTransient(company);
 
     CDOUserSavepoint savepoint2 = transaction.setSavepoint();
-    c1.setName("SIMON2");
+    company.setName("SIMON2");
     if (isPersisted)
     {
-      assertNotNull(transaction.getResourceSet().getEObject(uriC1, false));
+      assertNotNull(transaction.getResourceSet().getEObject(companyURI, false));
     }
     else
     {
-      assertNull(transaction.getResourceSet().getEObject(uriC1, false));
+      assertNull(transaction.getResourceSet().getEObject(companyURI, false));
     }
 
     savepoint2.rollback();
-    assertEquals("SIMON2", c1.getName());
-    assertTransient(c1);
+    assertEquals("SIMON2", company.getName());
+    assertTransient(company);
 
     if (isPersisted)
     {
-      assertNotNull(transaction.getResourceSet().getEObject(uriC1, false));
+      assertNotNull(transaction.getResourceSet().getEObject(companyURI, false));
     }
     else
     {
-      assertNull(transaction.getResourceSet().getEObject(uriC1, false));
+      assertNull(transaction.getResourceSet().getEObject(companyURI, false));
     }
 
     savepoint.rollback();
-    assertEquals("SIMON", c1.getName());
-    assertEquals(c1, transaction.getResourceSet().getEObject(uriC1, false));
+    assertEquals("SIMON", company.getName());
+    assertEquals(company, transaction.getResourceSet().getEObject(companyURI, false));
 
     if (isPersisted)
     {
-      assertDirty(c1, transaction);
+      assertDirty(company, transaction);
     }
     else
     {
-      assertNew(c1, transaction);
+      assertNew(company, transaction);
     }
 
     try
@@ -197,9 +197,9 @@
       CDOTransaction transaction = session.openTransaction();
       CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
 
-      Company c1 = getModel1Factory().createCompany();
-      c1.setName("Test");
-      resource.getContents().add(c1);
+      Company company = getModel1Factory().createCompany();
+      company.setName("Test");
+      resource.getContents().add(company);
 
       transaction.commit();
     }
@@ -208,14 +208,14 @@
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource = transaction.getOrCreateResource(getResourcePath("/my/resource"));
 
-    Company c1 = (Company)resource.getContents().get(0); // remove object by index
-    assertEquals("Test", c1.getName());
+    Company company = (Company)resource.getContents().get(0);
+    assertEquals("Test", company.getName());
 
-    resource.getContents().remove(0);
-    assertEquals("Test", c1.getName());
+    resource.getContents().remove(0); // Remove object by index
+    assertEquals("Test", company.getName());
 
     transaction.commit();
-    assertEquals("Test", c1.getName());
+    assertEquals("Test", company.getName());
   }
 
   private void detachResource(ResourceSet rset, CDOResource resource, boolean commitBeforeDelete) throws Exception
@@ -223,14 +223,14 @@
     CDOTransaction transaction = (CDOTransaction)resource.cdoView();
     Order order = getModel1Factory().createPurchaseOrder();
     OrderDetail orderDetail = getModel1Factory().createOrderDetail();
-    Product1 product1 = getModel1Factory().createProduct1();
-    product1.setName("product1");
+    Product1 product = getModel1Factory().createProduct1();
+    product.setName("product");
 
-    resource.getContents().add(product1);
+    resource.getContents().add(product);
     resource.getContents().add(order);
 
     order.getOrderDetails().add(orderDetail);
-    orderDetail.setProduct(product1);
+    orderDetail.setProduct(product);
     assertActive(resource);
     assertEquals(1, CDOUtil.getViewSet(rset).getViews().length);
     assertEquals(1, rset.getResources().size());// Bug 346636
@@ -246,17 +246,17 @@
     assertTransient(resource);
     assertTransient(order);
     assertTransient(orderDetail);
-    assertTransient(product1);
+    assertTransient(product);
 
     assertEquals(1, CDOUtil.getViewSet(rset).getViews().length);
     assertEquals(0, rset.getResources().size());// Bug 346636
     assertEquals(2, resource.getContents().size());
     assertEquals(true, resource.getContents().contains(order));
-    assertEquals(true, resource.getContents().contains(product1));
+    assertEquals(true, resource.getContents().contains(product));
     assertEquals(true, order.getOrderDetails().contains(orderDetail));
     assertEquals(order, orderDetail.eContainer());
     assertEquals(resource, ((InternalEObject)order).eDirectResource());
-    assertEquals(resource, ((InternalEObject)product1).eDirectResource());
+    assertEquals(resource, ((InternalEObject)product).eDirectResource());
 
     assertEquals(true && commitBeforeDelete, transaction.getDetachedObjects().containsKey(orderID));
     assertEquals(false, transaction.getRevisionDeltas().containsKey(orderID));
@@ -264,14 +264,10 @@
 
   public void testDetachNewResource() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
     ResourceSet rset = transaction.getResourceSet();
 
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
     detachResource(rset, resource, false);
 
@@ -280,15 +276,9 @@
 
   public void testDetachPersistedResource() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
     ResourceSet rset = transaction.getResourceSet();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
     transaction.commit();
@@ -301,15 +291,9 @@
 
   public void testDetachPersistedResourceWithPersistedData() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
     ResourceSet rset = transaction.getResourceSet();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
     transaction.commit();
@@ -322,16 +306,10 @@
 
   public void testDetachEmptyNewResource() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-    msg("Deleting resource");
     resource.delete(null);
     assertTransient(resource);
   }
@@ -375,27 +353,16 @@
   public void testDetachProxyResource() throws Exception
   {
     {
-      msg("Opening session");
       CDOSession session = openSession();
-
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Creating resource");
       transaction.createResource(getResourcePath("/test1"));
       transaction.commit();
     }
 
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
 
-    msg("Deleting resource");
     resource.delete(null);
     assertEquals(true, resource.isExisting());
     transaction.commit();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/InitialTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/InitialTest.java
index 3dc2be6..fbbe4e9 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/InitialTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/InitialTest.java
@@ -53,36 +53,25 @@
   {
     final Date date = new Date();
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
     assertTransient(supplier);
 
-    msg("Setting name");
     supplier.setName("Stepper");
     assertTransient(supplier);
-
-    msg("Verifying name");
     assertEquals("Stepper", supplier.getName());
     assertTransient(supplier);
 
-    msg("Creating purchaseOrder");
     PurchaseOrder purchaseOrder = getModel1Factory().createPurchaseOrder();
     assertTransient(purchaseOrder);
 
-    msg("Setting date");
     purchaseOrder.setDate(date);
     assertTransient(purchaseOrder);
-
-    msg("Verifying date");
     assertEquals(date, purchaseOrder.getDate());
     assertTransient(purchaseOrder);
 
-    msg("Setting supplier");
     purchaseOrder.setSupplier(supplier);
     assertTransient(supplier);
     assertTransient(purchaseOrder);
-
-    msg("Verifying supplier");
     assertEquals(supplier, purchaseOrder.getSupplier());
     assertTransient(supplier);
     assertTransient(purchaseOrder);
@@ -92,27 +81,22 @@
   {
     final URI uri = URI.createURI("cdo:/test1");
 
-    msg("Creating resourceSet");
     ResourceSet resourceSet = new ResourceSetImpl();
     SessionUtil.prepareResourceSet(resourceSet);
 
-    msg("Creating resource");
     CDOResource resource = (CDOResource)resourceSet.createResource(uri);
     assertTransient(resource);
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
     assertTransient(supplier);
     assertEquals(null, supplier.eContainer());
 
-    msg("Verifying contents");
     EList<EObject> contents = resource.getContents();
     assertNotNull(contents);
     assertEquals(true, contents.isEmpty());
     assertEquals(0, contents.size());
     assertTransient(resource);
 
-    msg("Adding supplier");
     contents.add(supplier);
     assertTransient(resource);
     assertTransient(supplier);
@@ -126,7 +110,6 @@
 
   public void testOpenSession() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
     assertNotNull(session);
     assertEquals(false, session.isClosed());
@@ -136,16 +119,13 @@
 
   public void testStartTransaction() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
     assertNotNull(transaction);
     assertEquals(session, transaction.getSession());
   }
 
-  public void testAttachResource() throws Exception
+  public void testCreateResource() throws Exception
   {
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
@@ -180,25 +160,14 @@
 
   public void testCommitNew() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
-
-    msg("Setting name");
     supplier.setName("Stepper");
-
-    msg("Adding supplier");
     resource.getContents().add(supplier);
 
-    msg("Committing");
     CDOCommitInfo commit = transaction.commit();
     assertEquals(CDOState.CLEAN, resource.cdoState());
     assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(supplier).cdoState());
@@ -209,28 +178,16 @@
 
   public void testReadResourceClean() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
-
-    msg("Setting name");
     supplier.setName("Stepper");
-
-    msg("Adding supplier");
     resource.getContents().add(supplier);
 
-    msg("Committing");
     long commitTime = transaction.commit().getTimeStamp();
 
-    msg("Getting supplier");
     EList<EObject> contents = resource.getContents();
     Supplier s = (Supplier)contents.get(0);
     assertEquals(1, CDOUtil.getCDOObject(s).cdoRevision().getVersion());
@@ -240,62 +197,34 @@
 
   public void testReadObjectClean() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
-
-    msg("Setting name");
     supplier.setName("Stepper");
-
-    msg("Adding supplier");
     resource.getContents().add(supplier);
 
-    msg("Committing");
     transaction.commit();
 
-    msg("Getting supplier");
     Supplier s = (Supplier)resource.getContents().get(0);
     assertEquals(1, CDOUtil.getCDOObject(s).cdoRevision().getVersion());
-
-    msg("Verifying name");
     assertEquals("Stepper", s.getName());
   }
 
   public void testWriteClean() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Creating resource");
     CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-    msg("Creating supplier");
     Supplier supplier = getModel1Factory().createSupplier();
-
-    msg("Setting name");
     supplier.setName("Stepper");
-
-    msg("Adding supplier");
     resource.getContents().add(supplier);
 
-    msg("Committing");
     transaction.commit();
 
-    msg("Getting supplier");
     Supplier s = (Supplier)resource.getContents().get(0);
-
-    msg("Setting name");
     s.setName("Eike");
     assertEquals("Eike", s.getName());
     assertEquals(CDOState.DIRTY, CDOUtil.getCDOObject(supplier).cdoState());
@@ -328,37 +257,25 @@
 
   public void testGetResource() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
 
     {
       disableConsole();
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Creating resource");
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating supplier");
       Supplier supplier = getModel1Factory().createSupplier();
-
-      msg("Setting name");
       supplier.setName("Stepper");
-
-      msg("Adding supplier");
       resource.getContents().add(supplier);
 
-      msg("Committing");
       transaction.commit();
       enableConsole();
     }
 
     {
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Getting resource");
       CDOResource resource = transaction.getResource(getResourcePath("/test1"), true);
+
       assertNotNull(resource);
       assertEquals(URI.createURI("cdo://" + session.getRepositoryInfo().getUUID() + getResourcePath("/test1")),
           resource.getURI());
@@ -370,12 +287,10 @@
     }
 
     {
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Getting resource");
       CDOResource resource = (CDOResource)transaction.getResourceSet().getResource(
           CDOURIUtil.createResourceURI(transaction, getResourcePath("/test1")), true);
+
       assertNotNull(resource);
       assertEquals(URI.createURI("cdo://" + session.getRepositoryInfo().getUUID() + getResourcePath("/test1")),
           resource.getURI());
@@ -389,38 +304,25 @@
 
   public void testGetContents() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
 
     {
       disableConsole();
-      msg("Opening transaction");
-      CDOTransaction transaction = session.openTransaction();
 
-      msg("Creating resource");
+      CDOTransaction transaction = session.openTransaction();
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating supplier");
       Supplier supplier = getModel1Factory().createSupplier();
-
-      msg("Setting name");
       supplier.setName("Stepper");
-
-      msg("Adding supplier");
       resource.getContents().add(supplier);
 
-      msg("Committing");
       transaction.commit();
       enableConsole();
     }
 
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Getting resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
 
-    msg("Getting contents");
     EList<EObject> contents = resource.getContents();
     assertNotNull(contents);
 
@@ -430,45 +332,28 @@
 
   public void testReadObjectProxy() throws Exception
   {
-    msg("Opening session");
     CDOSession session = openSession();
 
     {
       disableConsole();
-      msg("Opening transaction");
-      CDOTransaction transaction = session.openTransaction();
 
-      msg("Creating resource");
+      CDOTransaction transaction = session.openTransaction();
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating supplier");
       Supplier supplier = getModel1Factory().createSupplier();
-
-      msg("Setting name");
       supplier.setName("Stepper");
-
-      msg("Adding supplier");
       resource.getContents().add(supplier);
 
-      msg("Committing");
       transaction.commit();
       enableConsole();
     }
 
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Getting resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
-
-    msg("Getting contents");
     EList<EObject> contents = resource.getContents();
 
-    msg("Getting supplier");
     Supplier s = (Supplier)contents.get(0);
     assertNotNull(s);
-
-    msg("Verifying name");
     assertEquals("Stepper", s.getName());
   }
 
@@ -478,6 +363,7 @@
 
     {
       disableConsole();
+
       CDOTransaction transaction = session.openTransaction();
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
@@ -486,7 +372,6 @@
       product.setName("McDuff");
 
       resource.getContents().add(product);
-
       assertEquals("DESCRIPTION", product.getDescription());
 
       transaction.commit();
@@ -499,7 +384,6 @@
     EList<EObject> contents = resource.getContents();
     Product1 s = (Product1)contents.get(0);
     assertNotNull(s);
-
     assertEquals("McDuff", s.getName());
     assertNull(s.getDescription());
 
@@ -510,38 +394,19 @@
   public void testLoadResource() throws Exception
   {
     {
-      // disableConsole();
-      msg("Opening session");
       CDOSession session = openSession();
-
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Creating resource");
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating supplier");
       Supplier supplier = getModel1Factory().createSupplier();
-
-      msg("Setting name");
       supplier.setName("Stepper");
-
-      msg("Adding supplier");
       resource.getContents().add(supplier);
 
-      msg("Committing");
       transaction.commit();
-      // XXX session.close();
-      // enableConsole();
     }
 
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Getting resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
     assertNotNull(resource);
 
@@ -557,48 +422,24 @@
   public void testLoadObject() throws Exception
   {
     {
-      // disableConsole();
-      msg("Opening session");
       CDOSession session = openSession();
-
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Creating resource");
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating supplier");
       Supplier supplier = getModel1Factory().createSupplier();
-
-      msg("Setting name");
       supplier.setName("Stepper");
-
-      msg("Adding supplier");
       resource.getContents().add(supplier);
 
-      msg("Committing");
       transaction.commit();
-      // XXX session.close();
-      enableConsole();
     }
 
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Getting resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
-
-    msg("Getting contents");
     EList<EObject> contents = resource.getContents();
 
-    msg("Getting supplier");
     Supplier s = (Supplier)contents.get(0);
     assertNotNull(s);
-
-    msg("Verifying name");
     assertEquals("Stepper", s.getName());
   }
 
@@ -665,49 +506,26 @@
   public void testNullReference() throws Exception
   {
     {
-      msg("Opening session");
       CDOSession session = openSession();
-
-      msg("Opening transaction");
       CDOTransaction transaction = session.openTransaction();
-
-      msg("Creating resource");
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
 
-      msg("Creating orderDetail");
       OrderDetail orderDetail = getModel1Factory().createOrderDetail();
-
-      msg("Setting price");
       orderDetail.setPrice(4.75f);
-
-      msg("Adding orderDetail");
       resource.getContents().add(orderDetail);
 
-      msg("Committing");
       transaction.commit();
       session.close();
     }
 
-    msg("Opening session");
     CDOSession session = openSession();
-
-    msg("Opening transaction");
     CDOTransaction transaction = session.openTransaction();
-
-    msg("Getting resource");
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
-
-    msg("Getting contents");
     EList<EObject> contents = resource.getContents();
 
-    msg("Getting supplier");
     OrderDetail orderDetail = (OrderDetail)contents.get(0);
     assertNotNull(orderDetail);
-
-    msg("Verifying price");
     assertEquals(4.75f, orderDetail.getPrice());
-
-    msg("Verifying product");
     assertEquals(null, orderDetail.getProduct());
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/PartialCommitTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/PartialCommitTest.java
index f2eba5c..f43d80a 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/PartialCommitTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/PartialCommitTest.java
@@ -11,6 +11,7 @@
 package org.eclipse.emf.cdo.tests;
 
 import org.eclipse.emf.cdo.CDOObject;
+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.server.IRepository;
@@ -293,44 +294,44 @@
     cat.getProducts().remove(product);
 
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(company4)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(po)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(product)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(true, tx.isDirty());
 
     tx.setCommittables(createSet(company4, resource1));
     tx.commit();
 
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(company4)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(po)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(po)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(po)) != null);
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(product)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(product)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(product)) != null);
     assertEquals(true, tx.isDirty());
 
     tx.setCommittables(createSet(po, company2));
     tx.commit();
 
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(company4)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(po)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(po)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(po)) != null);
     assertEquals(true, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(product)));
-    assertEquals(true, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(product)));
+    assertEquals(true, tx.getCleanRevision(CDOUtil.getCDOObject(product)) != null);
     assertEquals(true, tx.isDirty());
 
     tx.setCommittables(createSet(product, cat));
     tx.commit();
 
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(company4)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(company4)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(company4)) != null);
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(po)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(po)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(po)) != null);
     assertEquals(false, tx.getDetachedObjects().containsValue(CDOUtil.getCDOObject(product)));
-    assertEquals(false, tx.getCleanRevisions().containsKey(CDOUtil.getCDOObject(product)));
+    assertEquals(false, tx.getCleanRevision(CDOUtil.getCDOObject(product)) != null);
     assertEquals(true, tx.isDirty());
 
     tx.setCommittables(createSet(company1));
@@ -345,7 +346,7 @@
     company1.setName("Company");
 
     tx.setCommittables(createSet(supplier1));
-    tx.commit();
+    CDOCommitInfo commitInfo = tx.commit();
 
     assertDirty(company1, tx);
     assertEquals(company1.getName(), "Company");
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java
index ab6fa6c..fd98e4b 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java
@@ -445,26 +445,25 @@
 
   public void testRemoveResourceByIndex() throws Exception
   {
-    final int trees = 5;
-    final int depth = 5;
-    int count = 0;
+    final int trees = 4;
+    final int depth = 3;
+
+    final int treeToRemove = 3;
+    int treeSize;
 
     {
       CDOSession session = openSession();
       CDOTransaction transaction = session.openTransaction();
       CDOResource resource = transaction.createResource(getResourcePath("/test1"));
+
+      EList<EObject> contents = resource.getContents();
       for (int i = 0; i < trees; i++)
       {
-        Category tree = createCategoryTree(depth);
-        if (count == 0)
-        {
-          count = 1 + countObjects(tree);
-        }
-
-        resource.getContents().add(tree);
+        contents.add(createCategoryTree(depth));
       }
 
       transaction.commit();
+      treeSize = countObjects(contents.get(treeToRemove));
       session.close();
     }
 
@@ -474,15 +473,18 @@
 
     CDOResource resource = transaction.getResource(getResourcePath("/test1"));
     EList<EObject> contents = resource.getContents();
-    int expected = ((InternalCDOTransaction)transaction).getObjects().size() + count;
 
-    contents.remove(3);
-    assertEquals(expected, ((InternalCDOTransaction)transaction).getObjects().size());
+    EObject keepObjectInMemory = contents.remove(treeToRemove);
+    assertEquals(1, ((InternalCDOTransaction)transaction).getObjects().size());
+    assertEquals(1 + treeSize, ((InternalCDOTransaction)transaction).getLastSavepoint().getChangeInfos().size());
+    assertEquals(treeSize, ((InternalCDOTransaction)transaction).getLastSavepoint().getDetachedInfos().size());
+
+    keepObjectInMemory.getClass(); // Prevent compiler optimizations
   }
 
   private int countObjects(EObject tree)
   {
-    int count = 0;
+    int count = 1; // Root
     for (TreeIterator<EObject> it = tree.eAllContents(); it.hasNext();)
     {
       it.next();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java
index 261ed8d..7d53b96 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java
@@ -27,6 +27,7 @@
 import org.eclipse.emf.cdo.util.CDOUtil;
 
 import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.io.IOUtil;
 
@@ -189,117 +190,6 @@
 
   // ///////////////////////////////////////////////////
 
-  public void test_PREPARED_with_ATTACH() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      testAttach(CDOUtil.getCDOObject(supplier));
-      fail("Expected NullPointerException due to revision==null");
-    }
-    catch (NullPointerException ex)
-    {
-    }
-  }
-
-  public void test_PREPARED_with_DETACH() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      detach(supplier);
-      fail("IllegalStateException expected");
-    }
-    catch (IllegalStateException expected)
-    {
-      assertFailure(expected);
-    }
-  }
-
-  public void test_PREPARED_with_READ() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-    read(supplier);
-    assertEquals(CDOState.PREPARED, CDOUtil.getCDOObject(supplier).cdoState());
-  }
-
-  public void test_PREPARED_with_WRITE() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      write(supplier);
-      fail("IllegalStateException expected");
-    }
-    catch (IllegalStateException expected)
-    {
-      assertFailure(expected);
-    }
-  }
-
-  public void test_PREPARED_with_INVALIDATE() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      invalidate(supplier);
-      fail("IllegalStateException expected");
-    }
-    catch (IllegalStateException expected)
-    {
-      assertFailure(expected);
-    }
-  }
-
-  public void test_PREPARED_with_COMMIT() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      commit(supplier, new CommitTransactionResult(null, BRANCH.getPoint(TIMESTAMP), CDOBranchPoint.UNSPECIFIED_DATE,
-          false));
-      fail("IllegalStateException expected");
-    }
-    catch (IllegalStateException expected)
-    {
-      assertFailure(expected);
-    }
-  }
-
-  public void test_PREPARED_with_ROLLBACK() throws Exception
-  {
-    Supplier supplier = getModel1Factory().createSupplier();
-    supplier.setName("Stepper");
-    setState(supplier, CDOState.PREPARED);
-
-    try
-    {
-      rollback(supplier);
-      fail("IllegalStateException expected");
-    }
-    catch (IllegalStateException expected)
-    {
-      assertFailure(expected);
-    }
-  }
-
   // ///////////////////////////////////////////////////
 
   public void test_NEW_with_ATTACH() throws Exception
@@ -418,7 +308,7 @@
   private static void assertFailure(IllegalStateException ex)
   {
     IOUtil.print(ex);
-    assertEquals("Expected FAIL transition", true, ex.getMessage().startsWith("Failing event "));
+    assertEquals("Expected FAIL transition", true, ex.getMessage().startsWith(CDOStateMachine2.State.FAIL_PREFIX));
   }
 
   private static void setState(EObject object, CDOState state)
@@ -435,7 +325,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.detach((InternalCDOObject)cdoObject);
+      CDOStateMachine2.INSTANCE.detach((InternalCDOObject)cdoObject);
     }
   }
 
@@ -444,7 +334,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.read((InternalCDOObject)cdoObject);
+      CDOStateMachine2.INSTANCE.read((InternalCDOObject)cdoObject);
     }
   }
 
@@ -453,7 +343,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.write((InternalCDOObject)cdoObject);
+      CDOStateMachine2.INSTANCE.write((InternalCDOObject)cdoObject, null);
     }
   }
 
@@ -462,7 +352,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.invalidate((InternalCDOObject)cdoObject, null);
+      CDOStateMachine2.INSTANCE.invalidate((InternalCDOObject)cdoObject, null);
     }
   }
 
@@ -471,7 +361,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.commit((InternalCDOObject)cdoObject, result);
+      CDOStateMachine2.INSTANCE.commit((InternalCDOObject)cdoObject, result);
     }
   }
 
@@ -480,7 +370,7 @@
     CDOObject cdoObject = CDOUtil.getCDOObject(object);
     if (cdoObject != null)
     {
-      CDOStateMachine.INSTANCE.rollback((InternalCDOObject)cdoObject);
+      CDOStateMachine2.INSTANCE.rollback((InternalCDOObject)cdoObject);
     }
   }
 
@@ -501,7 +391,7 @@
     {
       method = CDOStateMachine.class.getDeclaredMethod(methodName, new Class[] { InternalCDOObject.class });
       method.setAccessible(true);
-      method.invoke(CDOStateMachine.INSTANCE, object);
+      method.invoke(CDOStateMachine2.INSTANCE, object);
     }
     catch (RuntimeException ex)
     {
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_252214_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_252214_Test.java
index 65d6a0a..f129ad4 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_252214_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_252214_Test.java
@@ -21,7 +21,7 @@
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.view.CDOView;
 
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.emf.spi.cdo.InternalCDOObject;
 
@@ -75,7 +75,7 @@
       CDOResource auditResource = audit.getResource(getResourcePath("/res1"));
       Company auditCompany = (Company)auditResource.getContents().get(0);
       CDOObject cdoAuditCompany = CDOUtil.getCDOObject(auditCompany);
-      CDOStateMachine.INSTANCE.invalidate((InternalCDOObject)cdoAuditCompany, null);
+      CDOStateMachine2.INSTANCE.invalidate((InternalCDOObject)cdoAuditCompany, null);
     }
 
     audit.setTimeStamp(commitTime2);
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337587_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337587_Test.java
index 0a47bec..790266d 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337587_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_337587_Test.java
@@ -68,7 +68,7 @@
     salesOrders.set(5, salesOrder);
 
     CDOObject cdoResource = CDOUtil.getCDOObject(resource);
-    InternalCDORevision cleanRevision = ((InternalCDOTransaction)transaction).getCleanRevisions().get(cdoResource);
+    InternalCDORevision cleanRevision = ((InternalCDOTransaction)transaction).getCleanRevision(cdoResource);
 
     try
     {
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350027_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350027_Test.java
index 7b6b49f..e411971 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350027_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350027_Test.java
@@ -10,6 +10,7 @@
  */
 package org.eclipse.emf.cdo.tests.bugzilla;
 
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
 import org.eclipse.emf.cdo.internal.common.revision.delta.CDOMoveFeatureDeltaImpl;
 import org.eclipse.emf.cdo.tests.AbstractCDOTest;
 
@@ -26,7 +27,8 @@
   {
     // ABCD (before move)
     // ACDB (after move from 1 => 3)
-    CDOMoveFeatureDeltaImpl move = new CDOMoveFeatureDeltaImpl(getModel1Package().getAddress_Name(), 3, 1);
+    CDOMoveFeatureDeltaImpl move = new CDOMoveFeatureDeltaImpl(getModel1Package().getAddress_Name(), 3, 1,
+        CDOFeatureDelta.UNKNOWN_VALUE);
 
     // This simulates the removal of D which is at position 3 before the move
     move.adjustAfterRemoval(3);
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350987_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350987_Test.java
index 413e812..8e6b4bc 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350987_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_350987_Test.java
@@ -25,12 +25,10 @@
 import org.eclipse.emf.cdo.util.CommitException;
 
 import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.spi.cdo.InternalCDOObject;
 import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Bug 350987: Revision compare does not consider EObject values in references
@@ -76,9 +74,7 @@
     CDOObject cdoOrderDetail = CDOUtil.getCDOObject(orderDetail);
     CDORevision revision = cdoOrderDetail.cdoRevision();
 
-    Map<InternalCDOObject, InternalCDORevision> cleanRevisions = ((InternalCDOTransaction)transaction)
-        .getCleanRevisions();
-    InternalCDORevision originRevision = cleanRevisions.get(cdoOrderDetail);
+    InternalCDORevision originRevision = ((InternalCDOTransaction)transaction).getCleanRevision(cdoOrderDetail);
 
     CDORevisionDelta delta = revision.compare(originRevision);
 
@@ -97,7 +93,7 @@
 
     // Element shouldn't be added
     assertEquals(previousSize, product.getOrderDetails().size());
-    originRevision = cleanRevisions.get(cdoProduct);
+    originRevision = ((InternalCDOTransaction)transaction).getCleanRevision(cdoProduct);
     delta = revision.compare(originRevision);
 
     // Comparing with clean revision should not give changes
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_397822_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_397822_Test.java
index 9b87e73..89fc315 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_397822_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_397822_Test.java
@@ -70,10 +70,10 @@
 
     assertRevisionDeltaContainsListChanges(revisionDelta, reference //
         // removal of elem4 at index 3
-        , new CDORemoveFeatureDeltaImpl(reference, 3)
+        , new CDORemoveFeatureDeltaImpl(reference, 3, CDOFeatureDelta.UNKNOWN_VALUE)
 
         // removal of elem2 at index 1
-        , new CDORemoveFeatureDeltaImpl(reference, 1)
+        , new CDORemoveFeatureDeltaImpl(reference, 1, CDOFeatureDelta.UNKNOWN_VALUE)
 
     // TODO Clarify where the following delta is supposed to come from (see bug 390283)
     // // elem5 (at index 3 after the two removals) takes elem2's place at index 1
@@ -105,10 +105,10 @@
 
     assertRevisionDeltaContainsListChanges(revisionDelta, attribute //
         // removal of '4' at index 3
-        , new CDORemoveFeatureDeltaImpl(attribute, 3)
+        , new CDORemoveFeatureDeltaImpl(attribute, 3, CDOFeatureDelta.UNKNOWN_VALUE)
 
         // removal of '2' at index 1
-        , new CDORemoveFeatureDeltaImpl(attribute, 1)
+        , new CDORemoveFeatureDeltaImpl(attribute, 1, CDOFeatureDelta.UNKNOWN_VALUE)
 
     );
   }
diff --git a/plugins/org.eclipse.emf.cdo.ui.ide/src/org/eclipse/emf/cdo/ui/ide/RepositoryContentProvider.java b/plugins/org.eclipse.emf.cdo.ui.ide/src/org/eclipse/emf/cdo/ui/ide/RepositoryContentProvider.java
index a924b66..62ee9a6 100644
--- a/plugins/org.eclipse.emf.cdo.ui.ide/src/org/eclipse/emf/cdo/ui/ide/RepositoryContentProvider.java
+++ b/plugins/org.eclipse.emf.cdo.ui.ide/src/org/eclipse/emf/cdo/ui/ide/RepositoryContentProvider.java
@@ -35,7 +35,7 @@
 import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
 import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent;
 
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.container.ContainerEventAdapter;
 import org.eclipse.net4j.util.container.IContainer;
@@ -368,7 +368,7 @@
     {
       if (CDOUtil.isLegacyObject(cdoObject))
       {
-        CDOStateMachine.INSTANCE.read(cdoObject);
+        CDOStateMachine2.INSTANCE.read(cdoObject);
       }
 
       if (cdoObject instanceof CDOResource)
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
index fbc9bc2..7dbe0fd 100644
--- a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
@@ -34,7 +34,7 @@
 import org.eclipse.emf.cdo.view.CDOView;
 import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent;
 
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.event.IEvent;
 import org.eclipse.net4j.util.event.IListener;
@@ -1190,7 +1190,6 @@
                 switch (cdoObject.cdoState())
                 {
                 case TRANSIENT:
-                case PREPARED:
                 case INVALID:
                 case INVALID_CONFLICT:
                   it.remove();
@@ -1275,7 +1274,7 @@
         {
           if (CDOUtil.isLegacyObject(cdoObject))
           {
-            CDOStateMachine.INSTANCE.read(cdoObject);
+            CDOStateMachine2.INSTANCE.read(cdoObject);
           }
         }
 
diff --git a/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java b/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java
index ba0692a..fcbc9be 100644
--- a/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java
+++ b/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java
@@ -355,21 +355,22 @@
         InternalCDOTransaction tx = (InternalCDOTransaction)transaction;
         Set<CDOID> dirtyObjects = tx.getDirtyObjects().keySet();
         Set<CDOID> detachedObjects = tx.getDetachedObjects().keySet();
-        for (InternalCDORevision revision : tx.getCleanRevisions().values())
-        {
-          CDOID id = revision.getID();
-          boolean isDetached = detachedObjects.contains(id);
-
-          if (isDetached && base.isAddedObject(id))
-          {
-            base.deregisterObject(id);
-          }
-
-          if (dirtyObjects.contains(id) || isDetached)
-          {
-            base.registerChangedOrDetachedObject(revision);
-          }
-        }
+        int xxx;
+        // for (InternalCDORevision revision : tx.getCleanRevisions().values())
+        // {
+        // CDOID id = revision.getID();
+        // boolean isDetached = detachedObjects.contains(id);
+        //
+        // if (isDetached && base.isAddedObject(id))
+        // {
+        // base.deregisterObject(id);
+        // }
+        //
+        // if (dirtyObjects.contains(id) || isDetached)
+        // {
+        // base.registerChangedOrDetachedObject(revision);
+        // }
+        // }
 
         // Don't use keySet() because only the values() are ID-mapped!
         for (CDOObject object : tx.getNewObjects().values())
diff --git a/plugins/org.eclipse.emf.cdo/.settings/.api_filters b/plugins/org.eclipse.emf.cdo/.settings/.api_filters
index 705cffa..60aac05 100644
--- a/plugins/org.eclipse.emf.cdo/.settings/.api_filters
+++ b/plugins/org.eclipse.emf.cdo/.settings/.api_filters
@@ -1,5 +1,13 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.emf.cdo" version="2">
+    <resource path="META-INF/MANIFEST.MF">
+        <filter id="923795461">
+            <message_arguments>
+                <message_argument value="4.3.0"/>
+                <message_argument value="4.2.0"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="META-INF/MANIFEST.MF" type="org.eclipse.emf.cdo.spi.common.id.AbstractCDOIDByteArray">
         <filter id="305422471">
             <message_arguments>
@@ -132,6 +140,11 @@
                 <message_argument value="CDOSavepoint"/>
             </message_arguments>
         </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="isEmpty()"/>
+            </message_arguments>
+        </filter>
     </resource>
     <resource path="src/org/eclipse/emf/cdo/transaction/CDOTransaction.java" type="org.eclipse.emf.cdo.transaction.CDOTransaction">
         <filter id="571473929">
@@ -147,6 +160,20 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java" type="org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getReason()"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java" type="org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent$Reason">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="Reason"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/emf/cdo/view/CDOInvalidationPolicy.java" type="org.eclipse.emf.cdo.view.CDOInvalidationPolicy">
         <filter id="403767336">
             <message_arguments>
@@ -613,6 +640,39 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java" type="org.eclipse.emf.internal.cdo.view.CDOStateMachine2$Attached$Dirty">
+        <filter id="574668824">
+            <message_arguments>
+                <message_argument value="ChangeInfo"/>
+                <message_argument value="Dirty"/>
+                <message_argument value="CDOIDAndVersion"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java" type="org.eclipse.emf.internal.cdo.view.CDOStateMachine2$Attached$New">
+        <filter id="574668824">
+            <message_arguments>
+                <message_argument value="ChangeInfo"/>
+                <message_argument value="New"/>
+                <message_argument value="CDOIDAndVersion"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java" type="org.eclipse.emf.internal.cdo.view.CDOStateMachine2$Unattached$Detached">
+        <filter id="574619656">
+            <message_arguments>
+                <message_argument value="CDOIDAndVersion"/>
+                <message_argument value="Detached"/>
+            </message_arguments>
+        </filter>
+        <filter id="574668824">
+            <message_arguments>
+                <message_argument value="ChangeInfo"/>
+                <message_argument value="Detached"/>
+                <message_argument value="CDOIDAndVersion"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java" type="org.eclipse.emf.internal.cdo.view.CDOViewImpl$OptionsImpl">
         <filter id="574660632">
             <message_arguments>
@@ -667,4 +727,177 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java" type="org.eclipse.emf.spi.cdo.InternalCDOSavepoint">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="addChangeInfo(InternalCDOSavepoint.ChangeInfo)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="attachObject(InternalCDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getChangeInfo(CDOID)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getChangeInfos()"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getDetachedInfo(CDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getDetachedInfos()"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="isDetachedObject(CDOID)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="isInMemory()"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="removeChangeInfo(CDOID)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="removeChangeInfo(CDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="removeObject(InternalCDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="setInMemory(boolean)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java" type="org.eclipse.emf.spi.cdo.InternalCDOSavepoint$ChangeInfo">
+        <filter id="571473929">
+            <message_arguments>
+                <message_argument value="CDOIDAndVersion"/>
+                <message_argument value="ChangeInfo"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="ChangeInfo"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java" type="org.eclipse.emf.spi.cdo.InternalCDOTransaction">
+        <filter id="405901410">
+            <message_arguments>
+                <message_argument value="org.eclipse.emf.spi.cdo.InternalCDOTransaction"/>
+                <message_argument value="getCleanRevisions()"/>
+            </message_arguments>
+        </filter>
+        <filter id="405901410">
+            <message_arguments>
+                <message_argument value="org.eclipse.emf.spi.cdo.InternalCDOTransaction"/>
+                <message_argument value="registerAttached(InternalCDOObject, boolean)"/>
+            </message_arguments>
+        </filter>
+        <filter id="405901410">
+            <message_arguments>
+                <message_argument value="org.eclipse.emf.spi.cdo.InternalCDOTransaction"/>
+                <message_argument value="registerDirty(InternalCDOObject, CDOFeatureDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="405901410">
+            <message_arguments>
+                <message_argument value="org.eclipse.emf.spi.cdo.InternalCDOTransaction"/>
+                <message_argument value="registerFeatureDelta(InternalCDOObject, CDOFeatureDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="405901410">
+            <message_arguments>
+                <message_argument value="org.eclipse.emf.spi.cdo.InternalCDOTransaction"/>
+                <message_argument value="registerRevisionDelta(CDORevisionDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1209008130">
+            <message_arguments>
+                <message_argument value="4.0"/>
+                <message_argument value="4.3"/>
+                <message_argument value="_getCleanRevisions()"/>
+            </message_arguments>
+        </filter>
+        <filter id="1209008130">
+            <message_arguments>
+                <message_argument value="4.0"/>
+                <message_argument value="4.3"/>
+                <message_argument value="_registerAttached(InternalCDOObject, boolean)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="_registerDirty(InternalCDOObject, CDOFeatureDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="_registerFeatureDelta(InternalCDOObject, CDOFeatureDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="_registerRevisionDelta(CDORevisionDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="attachObject(InternalCDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="getCleanRevision(CDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="handleAttachingObject(CDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="handleDetachingObject(CDOObject)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="handleModifyingObject(CDOObject, CDOFeatureDelta)"/>
+            </message_arguments>
+        </filter>
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="updateDirtyState(boolean)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/emf/spi/cdo/InternalCDOView.java" type="org.eclipse.emf.spi.cdo.InternalCDOView">
+        <filter id="1211105284">
+            <message_arguments>
+                <message_argument value="dispatchLoadNotification(InternalCDOObject)"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOState.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOState.java
index 146aad0..8a8d184 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOState.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOState.java
@@ -31,7 +31,8 @@
   INVALID_CONFLICT,
 
   /**
-   * An intermediary state for internal use only. This state marks the first of two phases during an attach operation.
+   * Indicates that the object had been changed in a transaction, 
+   * but has then been changed back to its clean state.
    */
   PREPARED
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/eresource/impl/CDOResourceImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/eresource/impl/CDOResourceImpl.java
index 3ac5c5c..31d14e5 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/eresource/impl/CDOResourceImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/eresource/impl/CDOResourceImpl.java
@@ -31,7 +31,7 @@
 import org.eclipse.emf.cdo.view.CDOViewProviderRegistry;
 
 import org.eclipse.emf.internal.cdo.bundle.OM;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.WrappedException;
 import org.eclipse.net4j.util.collection.Pair;
@@ -347,7 +347,6 @@
     setPath(newPath);
   }
 
-
   @Override
   public Object eGet(int featureID, boolean resolve, boolean coreType)
   {
@@ -355,7 +354,7 @@
     {
     case EresourcePackage.CDO_RESOURCE__URI:
       return getURI();
-      
+
     default:
       return super.eGet(featureID, resolve, coreType);
     }
@@ -369,7 +368,7 @@
     case EresourcePackage.CDO_RESOURCE__URI:
       setURI((URI)newValue);
       break;
-      
+
     default:
       super.eSet(featureID, newValue);
     }
@@ -670,7 +669,6 @@
     try
     {
       EObject eObjectByFragment = getEObjectByFragment(uriFragment);
-
       if (eObjectByFragment != null)
       {
         return eObjectByFragment;
@@ -1497,7 +1495,7 @@
    */
   private void attached(InternalCDOObject cdoObject, InternalCDOTransaction transaction)
   {
-    CDOStateMachine.INSTANCE.attach(cdoObject, transaction);
+    CDOStateMachine2.INSTANCE.attach(cdoObject, transaction);
   }
 
   /**
@@ -1511,7 +1509,7 @@
       if (view instanceof InternalCDOTransaction) // Bug 376075
       {
         InternalCDOObject cdoObject = FSMUtil.adapt(object, view);
-        CDOStateMachine.INSTANCE.detach(cdoObject);
+        CDOStateMachine2.INSTANCE.detach(cdoObject);
       }
     }
   }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java
index eacfbee..5c2a19c 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java
@@ -196,11 +196,17 @@
             return CDOPushTransaction.this;
           }
 
+          @Deprecated
           public Type getType()
           {
             return Type.COMMITTED;
           }
 
+          public Reason getReason()
+          {
+            return Reason.COMMITTED;
+          }
+
           public Map<CDOID, CDOID> getIDMappings()
           {
             return Collections.emptyMap();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOSavepoint.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOSavepoint.java
index a572d1f..1be4cb9 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOSavepoint.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOSavepoint.java
@@ -39,15 +39,7 @@
 
   public CDOSavepoint getPreviousSavepoint();
 
-  /**
-   * @since 3.0
-   */
-  public boolean wasDirty();
-
-  /**
-   * @since 3.0
-   */
-  public Map<CDOID, CDORevision> getBaseNewObjects();
+  public boolean isEmpty();
 
   /**
    * @since 3.0
@@ -60,16 +52,52 @@
   public Map<CDOID, CDOObject> getDetachedObjects();
 
   /**
-   * Bug 283985 (Re-attachment)
+   * @since 3.0
+   */
+  public Map<CDOID, CDOObject> getDirtyObjects();
+
+  /**
+   * @since 4.2
+   */
+  public Map<CDOID, CDORevisionDelta> getRevisionDeltas2();
+
+  /**
+   * Return the list of new objects from this point without objects that are removed.
    *
    * @since 3.0
    */
-  public Map<CDOID, CDOObject> getReattachedObjects();
+  public Map<CDOID, CDOObject> getAllNewObjects();
+
+  /**
+   * Return the list of new objects from this point.
+   *
+   * @since 3.0
+   */
+  public Map<CDOID, CDOObject> getAllDirtyObjects();
 
   /**
    * @since 3.0
    */
-  public Map<CDOID, CDOObject> getDirtyObjects();
+  public Map<CDOID, CDOObject> getAllDetachedObjects();
+
+  /**
+   * Return the list of all deltas without objects that are removed.
+   *
+   * @since 3.0
+   */
+  public Map<CDOID, CDORevisionDelta> getAllRevisionDeltas();
+
+  /**
+   * @since 4.0
+   */
+  public CDOChangeSetData getAllChangeSetData();
+
+  /**
+   * @since 3.0
+   * @deprecated As of 4.3 no longer supported.
+   */
+  @Deprecated
+  public boolean wasDirty();
 
   /**
    * The returned map delegates to {@link #getRevisionDeltas2()} and does <b>not</b> support the following methods:
@@ -88,43 +116,25 @@
   public ConcurrentMap<CDOID, CDORevisionDelta> getRevisionDeltas();
 
   /**
-   * @since 4.2
+   * @since 3.0
+   * @deprecated As of 4.3 no longer supported.
    */
-  public Map<CDOID, CDORevisionDelta> getRevisionDeltas2();
+  @Deprecated
+  public Map<CDOID, CDORevision> getBaseNewObjects();
 
   /**
    * @since 3.0
+   * @deprecated As of 4.3 no longer supported.
    */
+  @Deprecated
   public Map<CDOID, CDORevision> getAllBaseNewObjects();
 
   /**
-   * Return the list of new objects from this point without objects that are removed.
+   * Bug 283985 (Re-attachment)
    *
    * @since 3.0
+   * @deprecated As of 4.3 no longer supported.
    */
-  public Map<CDOID, CDOObject> getAllNewObjects();
-
-  /**
-   * @since 3.0
-   */
-  public Map<CDOID, CDOObject> getAllDetachedObjects();
-
-  /**
-   * Return the list of new objects from this point.
-   *
-   * @since 3.0
-   */
-  public Map<CDOID, CDOObject> getAllDirtyObjects();
-
-  /**
-   * Return the list of all deltas without objects that are removed.
-   *
-   * @since 3.0
-   */
-  public Map<CDOID, CDORevisionDelta> getAllRevisionDeltas();
-
-  /**
-   * @since 4.0
-   */
-  public CDOChangeSetData getAllChangeSetData();
+  @Deprecated
+  public Map<CDOID, CDOObject> getReattachedObjects();
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java
index b2d4fb3..1e40cf3 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransactionFinishedEvent.java
@@ -27,18 +27,37 @@
  */
 public interface CDOTransactionFinishedEvent extends CDOViewEvent
 {
+  /**
+   * @deprecated As of 4.3 use {@link #getReason()}.
+   */
+  @Deprecated
   public Type getType();
 
+  public Reason getReason();
+
   public Map<CDOID, CDOID> getIDMappings();
 
   /**
-   * Enumerates the possible {@link CDOTransactionFinishedEvent#getType() causes} for a {@link CDOTransaction
-   * transaction} to become finished.
+   * Enumerates the possible {@link CDOTransactionFinishedEvent#getType() causes} for a 
+   * {@link CDOTransaction transaction} to become finished.
    * 
    * @author Eike Stepper
+   * @deprecated As of 4.3 use {@link Reason}. 
    */
+  @Deprecated
   public enum Type
   {
     COMMITTED, ROLLED_BACK
   }
+
+  /**
+   * Enumerates the possible {@link CDOTransactionFinishedEvent#getReason() causes} for a 
+   * {@link CDOTransaction transaction} to become finished.
+   * 
+   * @author Eike Stepper
+   */
+  public enum Reason
+  {
+    COMMITTED, ROLLED_BACK, UNDONE, MERGED
+  }
 }
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 21a7141..6ba92ae 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
@@ -49,7 +49,7 @@
 import org.eclipse.emf.internal.cdo.transaction.CDOXATransactionImpl;
 import org.eclipse.emf.internal.cdo.transaction.CDOXATransactionImpl.CDOXAInternalAdapter;
 import org.eclipse.emf.internal.cdo.view.CDORevisionPrefetchingPolicyImpl;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.AdapterUtil;
 import org.eclipse.net4j.util.container.IPluginContainer;
@@ -426,7 +426,7 @@
   public static void load(EObject eObject, CDOView view)
   {
     InternalCDOObject cdoObject = FSMUtil.adapt(eObject, view);
-    CDOStateMachine.INSTANCE.read(cdoObject);
+    CDOStateMachine2.INSTANCE.read(cdoObject);
 
     for (Iterator<InternalCDOObject> it = FSMUtil.iterator(cdoObject.eContents(), (InternalCDOView)view); it.hasNext();)
     {
@@ -479,7 +479,7 @@
       return null;
     }
 
-    CDORevision revision = CDOStateMachine.INSTANCE.read((InternalCDOObject)object);
+    CDORevision revision = CDOStateMachine2.INSTANCE.read((InternalCDOObject)object);
     return getRevisionByVersion(object, revision.getBranch(), version, revision);
   }
 
@@ -493,7 +493,7 @@
       return null;
     }
 
-    CDORevision revision = CDOStateMachine.INSTANCE.read((InternalCDOObject)object);
+    CDORevision revision = CDOStateMachine2.INSTANCE.read((InternalCDOObject)object);
     return getRevisionByVersion(object, branch, version, revision);
   }
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
index 3991de0..96eabcc 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
@@ -30,7 +30,7 @@
 import org.eclipse.emf.internal.cdo.bundle.OM;
 import org.eclipse.emf.internal.cdo.messages.Messages;
 import org.eclipse.emf.internal.cdo.object.CDOLockImpl;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.ObjectUtil;
 import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
@@ -198,7 +198,7 @@
   @Deprecated
   public final void cdoReload()
   {
-    CDOStateMachine.INSTANCE.reload(this);
+    // CDOStateMachine2.INSTANCE.reload(this);
   }
 
   /**
@@ -1186,7 +1186,7 @@
   }
 
   /**
-   * Adjust the reference ONLY if the opposite reference used CDOID. This is true ONLY if the state of <cdo>this</code>
+   * Adjust the reference ONLY if the opposite reference used CDOID. This is true ONLY if the state of <code>this</code>
    * was not {@link CDOState#NEW}.
    */
   private static void adjustOppositeReference(InternalCDOObject instance, InternalEObject object, EReference feature)
@@ -1632,7 +1632,7 @@
     {
       if (!FSMUtil.isTransient(CDOObjectImpl.this))
       {
-        CDOStateMachine.INSTANCE.read(CDOObjectImpl.this);
+        CDOStateMachine2.INSTANCE.read(CDOObjectImpl.this);
       }
     }
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyListener.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyListener.java
index 81b5f7c..4f63d6f 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyListener.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyListener.java
@@ -10,7 +10,7 @@
  */
 package org.eclipse.emf.internal.cdo.object;
 
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.emf.ecore.InternalEObject;
 
@@ -76,7 +76,7 @@
       try
       {
         handlingCallback = true;
-        CDOStateMachine.INSTANCE.read(this);
+        CDOStateMachine2.INSTANCE.read(this);
 
         // TODO Optimize this when the list position index is added to the new callbacks
         resolveAllProxies();
@@ -95,7 +95,7 @@
       try
       {
         handlingCallback = true;
-        CDOStateMachine.INSTANCE.write(this);
+        CDOStateMachine2.INSTANCE.write(this, null);
 
         // TODO Optimize this when the list position index is added to the new callbacks
         resolveAllProxies();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
index 1805087..9d7f701 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
@@ -35,7 +35,7 @@
 
 import org.eclipse.emf.internal.cdo.CDOObjectImpl;
 import org.eclipse.emf.internal.cdo.bundle.OM;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.ReflectUtil;
 import org.eclipse.net4j.util.WrappedException;
@@ -200,7 +200,7 @@
   @Deprecated
   public void cdoReload()
   {
-    CDOStateMachine.INSTANCE.reload(this);
+    // CDOStateMachine2.INSTANCE.reload(this);
   }
 
   public CDOObjectHistory cdoHistory()
@@ -305,7 +305,7 @@
    */
   public void cdoInternalPostRollback()
   {
-    CDOStateMachine.INSTANCE.read(this);
+    CDOStateMachine2.INSTANCE.read(this);
   }
 
   /**
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOCollectionLoadingPolicyImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOCollectionLoadingPolicyImpl.java
index 3bff4c3..242e6fa 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOCollectionLoadingPolicyImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOCollectionLoadingPolicyImpl.java
@@ -66,7 +66,7 @@
     doResolveProxy(revision, feature, 0, 0, Integer.MAX_VALUE);
   }
 
-  public Object resolveProxy(CDORevision rev, EStructuralFeature feature, int accessIndex, int serverIndex)
+  public Object resolveProxy(CDORevision revision, EStructuralFeature feature, int accessIndex, int serverIndex)
   {
     int chunkSize = resolveChunkSize;
     if (chunkSize == CDORevision.UNCHUNKED)
@@ -75,26 +75,27 @@
       chunkSize = Integer.MAX_VALUE;
     }
 
-    return doResolveProxy(rev, feature, accessIndex, serverIndex, chunkSize);
+    return doResolveProxy(revision, feature, accessIndex, serverIndex, chunkSize);
   }
 
-  private Object doResolveProxy(CDORevision rev, EStructuralFeature feature, int accessIndex, int serverIndex,
+  private Object doResolveProxy(CDORevision revision, EStructuralFeature feature, int accessIndex, int serverIndex,
       int chunkSize)
   {
     // Get proxy values
-    InternalCDORevision revision = (InternalCDORevision)rev;
-    int fetchIndex = serverIndex;
-
-    MoveableList<Object> list = revision.getList(feature);
+    InternalCDORevision rev = (InternalCDORevision)revision;
+    MoveableList<Object> list = rev.getList(feature);
     int size = list.size();
+
     int fromIndex = accessIndex;
     int toIndex = accessIndex;
+
     boolean minReached = false;
     boolean maxReached = false;
-    boolean alternation = false;
+    boolean towardsEnd = false;
+
     for (int i = 0; i < chunkSize; i++)
     {
-      if (alternation)
+      if (towardsEnd)
       {
         if (!maxReached && toIndex < size - 1 && list.get(toIndex + 1) instanceof CDOElementProxy)
         {
@@ -107,7 +108,7 @@
 
         if (!minReached)
         {
-          alternation = false;
+          towardsEnd = false;
         }
       }
       else
@@ -123,7 +124,7 @@
 
         if (!maxReached)
         {
-          alternation = true;
+          towardsEnd = true;
         }
       }
 
@@ -134,6 +135,6 @@
     }
 
     CDOSessionProtocol protocol = ((InternalCDOSession)session).getSessionProtocol();
-    return protocol.loadChunk(revision, feature, accessIndex, fetchIndex, fromIndex, toIndex);
+    return protocol.loadChunk(rev, feature, accessIndex, serverIndex, fromIndex, toIndex);
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java
index d6050b5..a795d9b 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java
@@ -13,97 +13,59 @@
 package org.eclipse.emf.internal.cdo.transaction;
 
 import org.eclipse.emf.cdo.CDOObject;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
 import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
 import org.eclipse.emf.cdo.common.id.CDOID;
 import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
 import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
 import org.eclipse.emf.cdo.internal.common.commit.CDOChangeSetDataImpl;
-import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDOFeatureDelta;
 
-import org.eclipse.net4j.util.collection.MultiMap;
 import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
 
 import org.eclipse.emf.spi.cdo.CDOTransactionStrategy;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
 import org.eclipse.emf.spi.cdo.InternalCDOSavepoint;
+import org.eclipse.emf.spi.cdo.InternalCDOSavepoint.ChangeInfo.ChangeType;
 import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 /**
  * @author Simon McDuff
+ * @author Eike Stepper
  * @since 2.0
  */
 public class CDOSavepointImpl extends CDOUserSavepointImpl implements InternalCDOSavepoint
 {
-  private final InternalCDOTransaction transaction;
+  private final Map<CDOID, ChangeInfo> changeInfos = CDOIDUtil.createMap();
 
-  private Map<CDOID, CDORevision> baseNewObjects = CDOIDUtil.createMap();
+  private final Map<InternalCDOObject, ChangeInfo> detachedInfos = new WeakHashMap<InternalCDOObject, ChangeInfo>();
 
-  private Map<CDOID, CDOObject> newObjects = CDOIDUtil.createMap();
+  private boolean inMemory;
 
-  // Bug 283985 (Re-attachment)
-  private Map<CDOID, CDOObject> reattachedObjects = CDOIDUtil.createMap();
-
-  private Map<CDOID, CDOObject> detachedObjects = new HashMap<CDOID, CDOObject>()
-  {
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    public CDOObject put(CDOID key, CDOObject object)
-    {
-      synchronized (transaction)
-      {
-        baseNewObjects.remove(key);
-        newObjects.remove(key);
-        reattachedObjects.remove(key);
-        dirtyObjects.remove(key);
-        revisionDeltas.remove(key);
-        return super.put(key, object);
-      }
-    }
-  };
-
-  private Map<CDOID, CDOObject> dirtyObjects = CDOIDUtil.createMap();
-
-  private Map<CDOID, CDORevisionDelta> revisionDeltas = new HashMap<CDOID, CDORevisionDelta>()
-  {
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    public CDORevisionDelta put(CDOID id, CDORevisionDelta delta)
-    {
-      transaction.clearResourcePathCacheIfNecessary(delta);
-      return super.put(id, delta);
-    }
-
-    @Override
-    public void putAll(Map<? extends CDOID, ? extends CDORevisionDelta> m)
-    {
-      throw new UnsupportedOperationException();
-    }
-  };
-
-  private boolean wasDirty;
+  private File storage;
 
   public CDOSavepointImpl(InternalCDOTransaction transaction, InternalCDOSavepoint lastSavepoint)
   {
     super(transaction, lastSavepoint);
-    this.transaction = transaction;
-    wasDirty = transaction.isDirty();
+    if (lastSavepoint != null)
+    {
+      for (ChangeInfo lastChangeInfo : lastSavepoint.getChangeInfos().values())
+      {
+        ChangeInfo changeInfo = lastChangeInfo.setSavepoint();
+        changeInfos.put(changeInfo.getID(), changeInfo);
+      }
+    }
   }
 
   @Override
@@ -115,7 +77,7 @@
   @Override
   public InternalCDOSavepoint getFirstSavePoint()
   {
-    synchronized (transaction)
+    synchronized (super.getTransaction())
     {
       return (InternalCDOSavepoint)super.getFirstSavePoint();
     }
@@ -124,7 +86,7 @@
   @Override
   public InternalCDOSavepoint getPreviousSavepoint()
   {
-    synchronized (transaction)
+    synchronized (super.getTransaction())
     {
       return (InternalCDOSavepoint)super.getPreviousSavepoint();
     }
@@ -133,66 +95,278 @@
   @Override
   public InternalCDOSavepoint getNextSavepoint()
   {
-    synchronized (transaction)
+    synchronized (super.getTransaction())
     {
       return (InternalCDOSavepoint)super.getNextSavepoint();
     }
   }
 
+  public ChangeInfo addChangeInfo(ChangeInfo changeInfo)
+  {
+    if (changeInfo.getType() == ChangeType.DETACHED)
+    {
+      detachedInfos.put(changeInfo.getObject(), changeInfo);
+    }
+
+    return changeInfos.put(changeInfo.getID(), changeInfo);
+  }
+
+  public ChangeInfo removeChangeInfo(CDOID id)
+  {
+    ChangeInfo changeInfo = changeInfos.remove(id);
+    if (changeInfo != null && changeInfo.getType() == ChangeType.DETACHED)
+    {
+      detachedInfos.remove(changeInfo.getObject());
+    }
+
+    return changeInfo;
+  }
+
+  public ChangeInfo removeChangeInfo(CDOObject object)
+  {
+    ChangeInfo changeInfo = detachedInfos.remove(object);
+    if (changeInfo != null)
+    {
+      changeInfos.remove(changeInfo.getID());
+    }
+
+    return changeInfo;
+  }
+
+  public ChangeInfo getChangeInfo(CDOID id)
+  {
+    return changeInfos.get(id);
+  }
+
+  public ChangeInfo getDetachedInfo(CDOObject object)
+  {
+    return detachedInfos.get(object);
+  }
+
+  public Map<CDOID, ChangeInfo> getChangeInfos()
+  {
+    return Collections.unmodifiableMap(changeInfos);
+  }
+
+  public Map<InternalCDOObject, ChangeInfo> getDetachedInfos()
+  {
+    return Collections.unmodifiableMap(detachedInfos);
+  }
+
   public void clear()
   {
-    synchronized (transaction)
+    synchronized (super.getTransaction())
     {
-      newObjects.clear();
-      dirtyObjects.clear();
-      revisionDeltas.clear();
-      baseNewObjects.clear();
-      detachedObjects.clear();
-      reattachedObjects.clear();
+      changeInfos.clear();
+      detachedInfos.clear();
     }
   }
 
-  public boolean wasDirty()
+  public boolean isInMemory()
   {
-    return wasDirty;
+    return storage == null;
+  }
+
+  public void setInMemory(boolean inMemory)
+  {
+    this.inMemory = inMemory;
+    if (isInMemory())
+    {
+      if (!inMemory)
+      {
+        unload();
+      }
+    }
+    else
+    {
+      if (!inMemory)
+      {
+        load();
+      }
+    }
+  }
+
+  private void load()
+  {
+  }
+
+  private void unload()
+  {
+  }
+
+  public boolean isEmpty()
+  {
+    return changeInfos.isEmpty();
+  }
+
+  public Map<CDOID, CDOObject> getObjects(ChangeInfo.ChangeType type, Map<CDOID, ChangeInfo> baseline)
+  {
+    Map<CDOID, CDOObject> result = CDOIDUtil.createMap();
+    for (ChangeInfo changeInfo : changeInfos.values())
+    {
+      if (changeInfo.getType() == type)
+      {
+        InternalCDOObject object = changeInfo.getObject();
+        if (object != null)
+        {
+          CDOID id = changeInfo.getID();
+          if (baseline != null)
+          {
+            ChangeInfo baselineInfo = baseline.get(id);
+            if (baselineInfo != null && baselineInfo.getType() == type)
+            {
+              // Ignore the object if it has the same change kind in the baseline
+              continue;
+            }
+          }
+
+          result.put(id, object);
+        }
+      }
+    }
+
+    return Collections.unmodifiableMap(result);
   }
 
   public Map<CDOID, CDOObject> getNewObjects()
   {
-    return newObjects;
-  }
-
-  public Map<CDOID, CDOObject> getDetachedObjects()
-  {
-    return detachedObjects;
-  }
-
-  // Bug 283985 (Re-attachment)
-  public Map<CDOID, CDOObject> getReattachedObjects()
-  {
-    return reattachedObjects;
+    InternalCDOSavepoint previousSavepoint = getPreviousSavepoint();
+    Map<CDOID, ChangeInfo> baseline = previousSavepoint == null ? null : previousSavepoint.getChangeInfos();
+    return getObjects(ChangeType.NEW, baseline);
   }
 
   public Map<CDOID, CDOObject> getDirtyObjects()
   {
-    return dirtyObjects;
+    InternalCDOSavepoint previousSavepoint = getPreviousSavepoint();
+    Map<CDOID, ChangeInfo> baseline = previousSavepoint == null ? null : previousSavepoint.getChangeInfos();
+    return getObjects(ChangeType.DIRTY, baseline);
   }
 
-  @Deprecated
-  public Set<CDOID> getSharedDetachedObjects()
+  public Map<CDOID, CDOObject> getDetachedObjects()
   {
+    InternalCDOSavepoint previousSavepoint = getPreviousSavepoint();
+    Map<CDOID, ChangeInfo> baseline = previousSavepoint == null ? null : previousSavepoint.getChangeInfos();
+    return getObjects(ChangeType.DETACHED, baseline);
+  }
+
+  public Map<CDOID, CDORevisionDelta> getRevisionDeltas2()
+  {
+    int xxx;
     throw new UnsupportedOperationException();
   }
 
-  @Deprecated
-  public void recalculateSharedDetachedObjects()
+  public CDOChangeSetData getChangeSetData()
   {
+    int xxx;
     throw new UnsupportedOperationException();
   }
 
+  public CDOChangeSetData getAllChangeSetData()
+  {
+    synchronized (super.getTransaction())
+    {
+      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>();
+      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>();
+      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>();
+
+      for (ChangeInfo changeInfo : changeInfos.values())
+      {
+        switch (changeInfo.getType())
+        {
+        case NEW:
+          revisions.add(changeInfo.getObject().cdoRevision());
+          break;
+
+        case DIRTY:
+          deltas.add(changeInfo.getRevisionDelta());
+          break;
+
+        case DETACHED:
+          detached.add(changeInfo.getCleanRevision());
+        }
+      }
+
+      return new CDOChangeSetDataImpl(revisions, deltas, detached);
+    }
+  }
+
+  /**
+   * Return the list of all deltas without objects that are removed.
+   */
+  public Map<CDOID, CDORevisionDelta> getAllRevisionDeltas()
+  {
+    Map<CDOID, CDORevisionDelta> result = CDOIDUtil.createMap();
+    for (ChangeInfo changeInfo : changeInfos.values())
+    {
+      CDORevisionDelta revisionDelta = changeInfo.getRevisionDelta();
+      if (revisionDelta != null)
+      {
+        result.put(changeInfo.getID(), revisionDelta);
+      }
+    }
+
+    return Collections.unmodifiableMap(result);
+  }
+
+  /**
+   * Return the list of new objects from this point.
+   */
+  public Map<CDOID, CDOObject> getAllDirtyObjects()
+  {
+    return getObjects(ChangeType.DIRTY, null);
+  }
+
+  /**
+   * Return the list of new objects from this point without objects that are removed.
+   */
+  public Map<CDOID, CDOObject> getAllNewObjects()
+  {
+    return getObjects(ChangeType.NEW, null);
+  }
+
+  public Map<CDOID, CDOObject> getAllDetachedObjects()
+  {
+    return getObjects(ChangeType.DETACHED, null);
+  }
+
+  public boolean isNewObject(CDOID id)
+  {
+    if (id.isTemporary())
+    {
+      return true;
+    }
+
+    ChangeInfo changeInfo = changeInfos.get(id);
+    if (changeInfo != null && changeInfo.getType() == ChangeType.NEW)
+    {
+      return true;
+    }
+
+    return false;
+  }
+
+  public boolean isDetachedObject(CDOID id)
+  {
+    ChangeInfo changeInfo = changeInfos.get(id);
+    return changeInfo != null && changeInfo.getType() == ChangeType.DETACHED;
+  }
+
+  public void rollback()
+  {
+    InternalCDOTransaction transaction = getTransaction();
+    synchronized (transaction)
+    {
+      LifecycleUtil.checkActive(transaction);
+
+      CDOTransactionStrategy transactionStrategy = transaction.getTransactionStrategy();
+      transactionStrategy.rollback(transaction, this);
+    }
+  }
+
   @Deprecated
   public ConcurrentMap<CDOID, CDORevisionDelta> getRevisionDeltas()
   {
+    final Map<CDOID, CDORevisionDelta> revisionDeltas = getRevisionDeltas2();
     return new ConcurrentMap<CDOID, CDORevisionDelta>()
     {
       public int size()
@@ -289,277 +463,51 @@
     };
   }
 
-  public Map<CDOID, CDORevisionDelta> getRevisionDeltas2()
+  @Deprecated
+  public boolean wasDirty()
   {
-    return revisionDeltas;
+    throw new UnsupportedOperationException();
   }
 
-  public CDOChangeSetData getChangeSetData()
-  {
-    synchronized (transaction)
-    {
-      return createChangeSetData(newObjects, revisionDeltas, detachedObjects);
-    }
-  }
-
-  public CDOChangeSetData getAllChangeSetData()
-  {
-    synchronized (transaction)
-    {
-      return createChangeSetData(getAllNewObjects(), getAllRevisionDeltas(), getAllDetachedObjects());
-    }
-  }
-
-  private CDOChangeSetData createChangeSetData(Map<CDOID, CDOObject> newObjects,
-      Map<CDOID, CDORevisionDelta> revisionDeltas, Map<CDOID, CDOObject> detachedObjects)
-  {
-    List<CDOIDAndVersion> newList = new ArrayList<CDOIDAndVersion>(newObjects.size());
-    for (CDOObject object : newObjects.values())
-    {
-      newList.add(object.cdoRevision());
-    }
-
-    List<CDORevisionKey> changedList = new ArrayList<CDORevisionKey>(revisionDeltas.size());
-    for (CDORevisionDelta delta : revisionDeltas.values())
-    {
-      changedList.add(delta);
-    }
-
-    List<CDOIDAndVersion> detachedList = new ArrayList<CDOIDAndVersion>(detachedObjects.size());
-    for (CDOID id : detachedObjects.keySet())
-    {
-      detachedList.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
-    }
-
-    return new CDOChangeSetDataImpl(newList, changedList, detachedList);
-  }
-
+  @Deprecated
   public Map<CDOID, CDORevision> getBaseNewObjects()
   {
-    return baseNewObjects;
+    throw new UnsupportedOperationException();
   }
 
-  /**
-   * Return the list of new objects from this point.
-   */
-  public Map<CDOID, CDOObject> getAllDirtyObjects()
-  {
-    synchronized (transaction)
-    {
-      if (getPreviousSavepoint() == null)
-      {
-        return Collections.unmodifiableMap(getDirtyObjects());
-      }
-
-      MultiMap.ListBased<CDOID, CDOObject> dirtyObjects = new MultiMap.ListBased<CDOID, CDOObject>();
-      for (InternalCDOSavepoint savepoint = this; savepoint != null; savepoint = savepoint.getPreviousSavepoint())
-      {
-        dirtyObjects.getDelegates().add(savepoint.getDirtyObjects());
-      }
-
-      return dirtyObjects;
-    }
-  }
-
-  /**
-   * Return the list of new objects from this point without objects that are removed.
-   */
-  public Map<CDOID, CDOObject> getAllNewObjects()
-  {
-    synchronized (transaction)
-    {
-      if (getPreviousSavepoint() == null)
-      {
-        return Collections.unmodifiableMap(getNewObjects());
-      }
-
-      Map<CDOID, CDOObject> newObjects = CDOIDUtil.createMap();
-      for (InternalCDOSavepoint savepoint = getFirstSavePoint(); savepoint != null; savepoint = savepoint
-          .getNextSavepoint())
-      {
-        newObjects.putAll(savepoint.getNewObjects());
-        for (CDOID removedID : savepoint.getDetachedObjects().keySet())
-        {
-          newObjects.remove(removedID);
-        }
-      }
-
-      return newObjects;
-    }
-  }
-
-  /**
-   * @since 2.0
-   */
+  @Deprecated
   public Map<CDOID, CDORevision> getAllBaseNewObjects()
   {
-    synchronized (transaction)
-    {
-      if (getPreviousSavepoint() == null)
-      {
-        return Collections.unmodifiableMap(getBaseNewObjects());
-      }
-
-      MultiMap.ListBased<CDOID, CDORevision> newObjects = new MultiMap.ListBased<CDOID, CDORevision>();
-      for (InternalCDOSavepoint savepoint = this; savepoint != null; savepoint = savepoint.getPreviousSavepoint())
-      {
-        newObjects.getDelegates().add(savepoint.getBaseNewObjects());
-      }
-
-      return newObjects;
-    }
+    throw new UnsupportedOperationException();
   }
 
-  /**
-   * Return the list of all deltas without objects that are removed.
-   */
-  public Map<CDOID, CDORevisionDelta> getAllRevisionDeltas()
+  @Deprecated
+  public Map<CDOID, CDOObject> getReattachedObjects()
   {
-    synchronized (transaction)
-    {
-      if (getPreviousSavepoint() == null)
-      {
-        return Collections.unmodifiableMap(getRevisionDeltas2());
-      }
-
-      // We need to combined the result for all delta in different Savepoint
-      Map<CDOID, CDORevisionDelta> allRevisionDeltas = CDOIDUtil.createMap();
-      for (InternalCDOSavepoint savepoint = getFirstSavePoint(); savepoint != null; savepoint = savepoint
-          .getNextSavepoint())
-      {
-        for (CDORevisionDelta revisionDelta : savepoint.getRevisionDeltas2().values())
-        {
-          CDOID id = revisionDelta.getID();
-          if (!isNewObject(id))
-          {
-            CDORevisionDeltaImpl oldRevisionDelta = (CDORevisionDeltaImpl)allRevisionDeltas.get(id);
-            if (oldRevisionDelta == null)
-            {
-              allRevisionDeltas.put(id, revisionDelta.copy());
-            }
-            else
-            {
-              for (CDOFeatureDelta delta : revisionDelta.getFeatureDeltas())
-              {
-                CDOFeatureDelta copy = ((InternalCDOFeatureDelta)delta).copy();
-                oldRevisionDelta.addFeatureDelta(copy, null);
-              }
-            }
-          }
-        }
-
-        Set<CDOID> reattachedObjects = savepoint.getReattachedObjects().keySet();
-        for (CDOID detachedID : savepoint.getDetachedObjects().keySet())
-        {
-          if (!reattachedObjects.contains(detachedID))
-          {
-            allRevisionDeltas.remove(detachedID);
-          }
-        }
-      }
-
-      return Collections.unmodifiableMap(allRevisionDeltas);
-    }
+    throw new UnsupportedOperationException();
   }
 
-  public Map<CDOID, CDOObject> getAllDetachedObjects()
+  @Deprecated
+  public Set<CDOID> getSharedDetachedObjects()
   {
-    synchronized (transaction)
-    {
-      if (getPreviousSavepoint() == null && reattachedObjects.isEmpty())
-      {
-        return Collections.unmodifiableMap(getDetachedObjects());
-      }
-
-      Map<CDOID, CDOObject> detachedObjects = CDOIDUtil.createMap();
-      for (InternalCDOSavepoint savepoint = getFirstSavePoint(); savepoint != null; savepoint = savepoint
-          .getNextSavepoint())
-      {
-        for (Entry<CDOID, CDOObject> entry : savepoint.getDetachedObjects().entrySet())
-        {
-          CDOID detachedID = entry.getKey();
-          if (!isNewObject(detachedID))
-          {
-            CDOObject detachedObject = entry.getValue();
-            detachedObjects.put(detachedID, detachedObject);
-          }
-        }
-
-        for (CDOID reattachedID : savepoint.getReattachedObjects().keySet())
-        {
-          detachedObjects.remove(reattachedID);
-        }
-      }
-
-      return detachedObjects;
-    }
+    throw new UnsupportedOperationException();
   }
 
-  public boolean isNewObject(CDOID id)
+  @Deprecated
+  public void recalculateSharedDetachedObjects()
   {
-    if (id.isTemporary())
-    {
-      return true;
-    }
-
-    synchronized (transaction)
-    {
-      for (InternalCDOSavepoint savepoint = this; savepoint != null; savepoint = savepoint.getPreviousSavepoint())
-      {
-        if (savepoint.getNewObjects().containsKey(id))
-        {
-          return true;
-        }
-      }
-    }
-
-    return false;
+    throw new UnsupportedOperationException();
   }
 
-  // TODO Not sure if this new implementation is needed. The existing one passes all tests.
-  // public boolean isNewObject(CDOID id)
-  // {
-  // if (id.isTemporary())
-  // {
-  // return true;
-  // }
-  //
-  // boolean isNew = false;
-  // boolean wasNew = false;
-  // synchronized (transaction)
-  // {
-  // for (InternalCDOSavepoint savepoint = this; savepoint != null; savepoint = savepoint.getPreviousSavepoint())
-  // {
-  // if (savepoint.getNewObjects().containsKey(id))
-  // {
-  // isNew = true;
-  // wasNew = true;
-  // }
-  //
-  // if (isNew && savepoint.getDetachedObjects().containsKey(id))
-  // {
-  // isNew = false;
-  // }
-  //
-  // if (!isNew && wasNew && savepoint.getReattachedObjects().containsKey(id))
-  // {
-  // isNew = true;
-  // }
-  // }
-  // }
-  //
-  // return isNew;
-  // }
-
-  public void rollback()
+  @Deprecated
+  public void attachObject(InternalCDOObject object)
   {
-    synchronized (transaction)
-    {
-      InternalCDOTransaction transaction = getTransaction();
-      LifecycleUtil.checkActive(transaction);
+    throw new UnsupportedOperationException();
+  }
 
-      CDOTransactionStrategy transactionStrategy = transaction.getTransactionStrategy();
-      transactionStrategy.rollback(transaction, this);
-    }
+  @Deprecated
+  public void removeObject(InternalCDOObject object)
+  {
+    throw new UnsupportedOperationException();
   }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
index bcd2ace..baf3936 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
@@ -95,7 +95,7 @@
         throw new CommitException(rollbackMessage);
 
       default:
-        throw new IllegalStateException("Invalid rollbackreason: " + rollbackReason);
+        throw new IllegalStateException("Invalid rollback reason: " + rollbackReason);
       }
     }
 
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 360e004..c6f5df5 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
@@ -53,9 +53,7 @@
 import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
 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;
-import org.eclipse.emf.cdo.common.revision.delta.CDOOriginSizeProvider;
 import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
 import org.eclipse.emf.cdo.common.util.CDOException;
 import org.eclipse.emf.cdo.eresource.CDOBinaryResource;
@@ -80,6 +78,7 @@
 import org.eclipse.emf.cdo.spi.common.protocol.CDODataInputImpl;
 import org.eclipse.emf.cdo.spi.common.protocol.CDODataOutputImpl;
 import org.eclipse.emf.cdo.spi.common.revision.CDOIDMapper;
+import org.eclipse.emf.cdo.spi.common.revision.CDOReferenceAdjuster;
 import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
@@ -92,13 +91,13 @@
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 import org.eclipse.emf.cdo.transaction.CDOTransactionConflictEvent;
 import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent;
+import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent.Reason;
 import org.eclipse.emf.cdo.transaction.CDOTransactionHandler;
 import org.eclipse.emf.cdo.transaction.CDOTransactionHandler1;
 import org.eclipse.emf.cdo.transaction.CDOTransactionHandler2;
 import org.eclipse.emf.cdo.transaction.CDOTransactionHandler3;
 import org.eclipse.emf.cdo.transaction.CDOTransactionHandlerBase;
 import org.eclipse.emf.cdo.transaction.CDOTransactionStartedEvent;
-import org.eclipse.emf.cdo.transaction.CDOUserSavepoint;
 import org.eclipse.emf.cdo.util.CDOURIUtil;
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.util.CommitException;
@@ -117,7 +116,7 @@
 import org.eclipse.emf.internal.cdo.util.CommitIntegrityCheck;
 import org.eclipse.emf.internal.cdo.util.CompletePackageClosure;
 import org.eclipse.emf.internal.cdo.util.IPackageClosure;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 import org.eclipse.emf.internal.cdo.view.CDOViewImpl;
 
 import org.eclipse.net4j.util.CheckUtil;
@@ -145,7 +144,6 @@
 import org.eclipse.emf.ecore.EStructuralFeature.Setting;
 import org.eclipse.emf.ecore.impl.EClassImpl.FeatureSubsetSupplier;
 import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.Resource.Internal;
 import org.eclipse.emf.ecore.util.EContentsEList;
 import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator;
 import org.eclipse.emf.ecore.util.ECrossReferenceEList;
@@ -156,6 +154,8 @@
 import org.eclipse.emf.spi.cdo.FSMUtil;
 import org.eclipse.emf.spi.cdo.InternalCDOObject;
 import org.eclipse.emf.spi.cdo.InternalCDOSavepoint;
+import org.eclipse.emf.spi.cdo.InternalCDOSavepoint.ChangeInfo;
+import org.eclipse.emf.spi.cdo.InternalCDOSavepoint.ChangeInfo.ChangeType;
 import org.eclipse.emf.spi.cdo.InternalCDOSession;
 import org.eclipse.emf.spi.cdo.InternalCDOSession.MergeData;
 import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
@@ -242,9 +242,12 @@
   private Set<? extends EObject> committables;
 
   /**
-   * A map to hold a clean (i.e. unmodified) revision for objects that have been modified or detached.
-   */
-  private Map<InternalCDOObject, InternalCDORevision> cleanRevisions = new ResolvingRevisionMap();
+  * A map to hold a clean (i.e. unmodified) revision for objects that have been modified or detached.
+  */
+  // private Map<InternalCDOObject, InternalCDORevision> cleanRevisions = new HashMap<InternalCDOObject,
+  // InternalCDORevision>();
+
+  // private Map<InternalCDOObject, InternalCDORevision> _cleanRevisions = new ResolvingRevisionMap();
 
   public CDOTransactionImpl(CDOBranch branch)
   {
@@ -377,6 +380,36 @@
     }
   }
 
+  public void handleAttachingObject(CDOObject object)
+  {
+    CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    for (int i = 0; i < handlers.length; i++)
+    {
+      CDOTransactionHandler1 handler = handlers[i];
+      handler.attachingObject(this, object);
+    }
+  }
+
+  public void handleModifyingObject(CDOObject object, CDOFeatureDelta featureDelta)
+  {
+    CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    for (int i = 0; i < handlers.length; i++)
+    {
+      CDOTransactionHandler1 handler = handlers[i];
+      handler.modifyingObject(this, object, featureDelta);
+    }
+  }
+
+  public void handleDetachingObject(CDOObject object)
+  {
+    CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    for (int i = 0; i < handlers.length; i++)
+    {
+      CDOTransactionHandler1 handler = handlers[i];
+      handler.detachingObject(this, object);
+    }
+  }
+
   @Override
   public boolean isDirty()
   {
@@ -388,9 +421,52 @@
     return dirty;
   }
 
+  public IListener[] updateDirtyState(boolean undone)
+  {
+    if (lastSavepoint.isEmpty())
+    {
+      // Becomes clean
+      if (dirty)
+      {
+        dirty = false;
+
+        IListener[] listeners = getListeners();
+        if (undone)
+        {
+          if (listeners != null)
+          {
+            fireEvent(new FinishedEvent(Reason.UNDONE, null), listeners);
+          }
+        }
+        else
+        {
+          // Let the caller fire a specific CDOTransactionFinishedEvent
+          return listeners;
+        }
+      }
+    }
+    else
+    {
+      // Becomes dirty
+      if (!dirty)
+      {
+        dirty = true;
+
+        IListener[] listeners = getListeners();
+        if (listeners != null)
+        {
+          fireEvent(new StartedEvent(), listeners);
+        }
+      }
+    }
+
+    return null;
+  }
+
+  @Deprecated
   public void setDirty(boolean dirty)
   {
-    this.dirty = dirty;
+    throw new UnsupportedOperationException();
   }
 
   @Override
@@ -437,9 +513,8 @@
     return conflicts;
   }
 
-  public synchronized CDOChangeSetData getChangeSetData()
+  public CDOChangeSetData getChangeSetData()
   {
-    checkActive();
     return lastSavepoint.getAllChangeSetData();
   }
 
@@ -524,8 +599,14 @@
     Map<CDOID, InternalCDORevision> oldRevisions = applyChangedObjects(changeSetData.getChangedObjects(),
         ancestorProvider, targetProvider, keepVersions, resultData.getChangedObjects());
 
+    IListener[] listeners = updateDirtyState(false);
+    if (listeners != null)
+    {
+      fireEvent(new FinishedEvent(Reason.MERGED, null), listeners);
+    }
+
     // Delta notifications
-    Collection<CDORevisionDelta> notificationDeltas = lastSavepoint.getRevisionDeltas2().values();
+    Collection<CDORevisionDelta> notificationDeltas = lastSavepoint.getAllRevisionDeltas().values();
     if (!notificationDeltas.isEmpty() || !detachedSet.isEmpty())
     {
       sendDeltaNotifications(notificationDeltas, detachedSet, oldRevisions);
@@ -594,10 +675,9 @@
         object.cdoInternalSetState(CDOState.NEW);
         object.cdoInternalPostLoad();
 
-        registerObject(object);
-        registerAttached(object, true);
+        int xxx;
+        // registerAttached(object, true);
         result.add(revision);
-        dirty = true;
       }
     }
   }
@@ -612,9 +692,8 @@
       if (object != null)
       {
         result.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
-        CDOStateMachine.INSTANCE.detach(object);
+        CDOStateMachine2.INSTANCE.detach(object);
         detachedSet.add(object);
-        dirty = true;
       }
     }
 
@@ -627,10 +706,6 @@
   {
     Map<CDOID, InternalCDORevision> oldRevisions = CDOIDUtil.createMap();
 
-    Map<CDOID, CDOObject> detachedObjects = lastSavepoint.getDetachedObjects();
-    Map<CDOID, CDOObject> dirtyObjects = lastSavepoint.getDirtyObjects();
-    Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
-
     for (CDORevisionKey key : changedObjects)
     {
       InternalCDORevisionDelta ancestorGoalDelta = (InternalCDORevisionDelta)key;
@@ -671,21 +746,17 @@
           throw new ChangeSetOutdatedException();
         }
 
-        revisionDeltas.put(id, targetGoalDelta);
         result.add(targetGoalDelta);
 
-        // handle reattached objects.
-        if (detachedObjects.containsKey(id))
+        // Handle reattached objects.
+        if (lastSavepoint.isDetachedObject(id))
         {
-          CDOStateMachine.INSTANCE.internalReattach(object, this);
+          CDOStateMachine2.INSTANCE.attach(object, this);
         }
 
         object.cdoInternalSetRevision(goalRevision);
         object.cdoInternalSetState(CDOState.DIRTY);
         revisionChanged = true;
-
-        dirtyObjects.put(id, object);
-        dirty = true;
       }
 
       if (revisionChanged)
@@ -793,8 +864,7 @@
     }
     else
     {
-      Map<CDOID, CDOObject> dirtyObjects = getLastSavepoint().getDirtyObjects();
-      setDirty(!dirtyObjects.isEmpty());
+      setDirty(!lastSavepoint.isEmpty());
     }
   }
 
@@ -1028,7 +1098,7 @@
     {
       if (node.isRoot())
       {
-        CDOStateMachine.INSTANCE.attach(node, this);
+        CDOStateMachine2.INSTANCE.attach(node, this);
       }
       else
       {
@@ -1046,7 +1116,7 @@
    */
   public synchronized void detach(CDOResourceImpl cdoResource)
   {
-    CDOStateMachine.INSTANCE.detach(cdoResource);
+    CDOStateMachine2.INSTANCE.detach(cdoResource);
   }
 
   /**
@@ -1120,7 +1190,7 @@
     }
 
     CDOID id = super.getRootOrTopLevelResourceNodeID(name);
-    if (getLastSavepoint().getAllDetachedObjects().containsKey(id) || getDirtyObjects().containsKey(id))
+    if (lastSavepoint.getAllDetachedObjects().containsKey(id) || getDirtyObjects().containsKey(id))
     {
       throw new CDOException(MessageFormat.format(Messages.getString("CDOTransactionImpl.1"), name)); //$NON-NLS-1$
     }
@@ -1157,10 +1227,17 @@
       return null;
     }
 
-    if (isObjectNew(id) && isObjectDetached(id))
-    {
-      throw new ObjectNotFoundException(id, this);
-    }
+    // ChangeInfo changeInfo = lastSavepoint.getChangeInfo(id);
+    // if (changeInfo != null)
+    // {
+    // return changeInfo.getObject();
+    // }
+
+    int xxx;
+    // if (/* isObjectNew(id) && */isObjectDetached(id))
+    // {
+    // throw new ObjectNotFoundException(id, this);
+    // }
 
     return super.getObject(id, loadOnDemand);
   }
@@ -1173,7 +1250,7 @@
 
   private boolean isObjectDetached(CDOID id)
   {
-    return lastSavepoint.getAllDetachedObjects().containsKey(id);
+    return lastSavepoint.isDetachedObject(id);
   }
 
   /**
@@ -1181,7 +1258,7 @@
    */
   public synchronized InternalCDOCommitContext createCommitContext()
   {
-    return new CDOCommitContextImpl(this);
+    return new CDOCommitContextImpl();
   }
 
   public/* synchronized */CDOCommitInfo commit() throws CommitException
@@ -1247,8 +1324,8 @@
     }
     finally
     {
-      getSession().endLocalCommit(token);
       clearResourcePathCacheIfNecessary(null);
+      getSession().endLocalCommit(token);
     }
   }
 
@@ -1262,7 +1339,7 @@
     CDOTransactionStrategy strategy = getTransactionStrategy();
     strategy.rollback(this, firstSavepoint);
 
-    cleanUp(null);
+    cleanUp(null, null);
   }
 
   private void removeObject(CDOID id, final CDOObject object)
@@ -1289,7 +1366,143 @@
     internal.cdoInternalSetState(CDOState.TRANSIENT);
   }
 
-  private Set<CDOID> rollbackCompletely(CDOUserSavepoint savepoint)
+  /**
+   * @author Eike Stepper
+   */
+  private static final class RollbackSupport
+  {
+    private static final int CLEAN = 0;
+
+    private static final int NEW = 1;
+
+    private static final int CHANGED = 2;
+
+    private static final int DETACHED = 3;
+
+    /**
+     * Finite state machine for rollback transitions.
+     */
+    private static final Transition[][] FSM = new Transition[4][4];
+
+    static
+    {
+      FSM[CLEAN][CLEAN] = Transition.FAIL;
+      FSM[CLEAN][NEW] = new UndoTransition();
+      FSM[CLEAN][CHANGED] = new UndoTransition();
+      FSM[CLEAN][DETACHED] = new UndoTransition();
+
+      FSM[NEW][CLEAN] = null;
+      FSM[NEW][NEW] = Transition.IGNORE;
+      FSM[NEW][CHANGED] = Transition.FAIL;
+      FSM[NEW][DETACHED] = Transition.FAIL;
+
+      FSM[CHANGED][CLEAN] = null;
+      FSM[CHANGED][NEW] = Transition.FAIL;
+      FSM[CHANGED][CHANGED] = Transition.IGNORE;
+      FSM[CHANGED][DETACHED] = null;
+
+      FSM[DETACHED][CLEAN] = null;
+      FSM[DETACHED][NEW] = Transition.FAIL;
+      FSM[DETACHED][CHANGED] = null;
+      FSM[DETACHED][DETACHED] = Transition.IGNORE;
+    }
+
+    public static Set<CDOID> rollbackTo(InternalCDOSavepoint formerSavepoint, InternalCDOSavepoint latestSavepoint)
+    {
+      Set<CDOID> idsOfNewObjectsWithDeltas = new HashSet<CDOID>();
+
+      Map<CDOID, ChangeInfo> latestChangeInfos = latestSavepoint.getChangeInfos();
+      Map<CDOID, ChangeInfo> formerChangeInfos = formerSavepoint.getChangeInfos();
+
+      // Handle all former infos and remove the corresponding latest infos
+      for (ChangeInfo formerChangeInfo : formerChangeInfos.values())
+      {
+        ChangeInfo latestChangeInfo = latestChangeInfos.remove(formerChangeInfo.getID());
+        rollbackTo(formerChangeInfo, latestChangeInfo);
+      }
+
+      // Handle remanining latest infos
+      for (ChangeInfo latestChangeInfo : latestChangeInfos.values())
+      {
+        ChangeInfo formerChangeInfo = formerChangeInfos.get(latestChangeInfo.getID());
+        rollbackTo(formerChangeInfo, latestChangeInfo);
+      }
+
+      return idsOfNewObjectsWithDeltas;
+    }
+
+    private static void rollbackTo(ChangeInfo formerChangeInfo, ChangeInfo latestChangeInfo)
+    {
+      int formerKind = getIndex(formerChangeInfo);
+      int latestKind = getIndex(latestChangeInfo);
+
+      Transition transition = FSM[formerKind][latestKind];
+      if (transition != null)
+      {
+        transition.execute(formerChangeInfo, latestChangeInfo);
+      }
+    }
+
+    private static int getIndex(ChangeInfo changeInfo)
+    {
+      if (changeInfo == null)
+      {
+        return CLEAN;
+      }
+
+      ChangeType type = changeInfo.getType();
+      switch (type)
+      {
+      case NEW:
+        return NEW;
+
+      case DIRTY:
+        return CHANGED;
+
+      case DETACHED:
+        return DETACHED;
+
+      default:
+        throw new IllegalStateException("Illegal change kind: " + type);
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public interface Transition
+    {
+      public static final Transition IGNORE = new Transition()
+      {
+        public void execute(ChangeInfo formerChangeInfo, ChangeInfo latestChangeInfo)
+        {
+          // Do nothing
+        }
+      };
+
+      public static final Transition FAIL = new Transition()
+      {
+        public void execute(ChangeInfo formerChangeInfo, ChangeInfo latestChangeInfo)
+        {
+          throw new IllegalStateException("Impossible to rollback from " + latestChangeInfo + " to " + formerChangeInfo);
+        }
+      };
+
+      public void execute(ChangeInfo formerChangeInfo, ChangeInfo latestChangeInfo);
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    private static final class UndoTransition implements Transition
+    {
+      public void execute(ChangeInfo formerChangeInfo, ChangeInfo latestChangeInfo)
+      {
+      }
+    }
+  }
+
+  private Set<CDOID> rollbackCompletely(InternalCDOSavepoint savepoint)
   {
     Set<CDOID> idsOfNewObjectsWithDeltas = new HashSet<CDOID>();
 
@@ -1330,7 +1543,7 @@
         if (idOrObject instanceof CDOObjectImpl)
         {
           CDOObjectImpl impl = (CDOObjectImpl)idOrObject;
-          Internal directResource = impl.eDirectResource();
+          Resource.Internal directResource = impl.eDirectResource();
           EObject container = impl.eContainer();
           if (!toBeDetached.contains(directResource) && !toBeDetached.contains(container))
           {
@@ -1341,7 +1554,7 @@
         else if (idOrObject instanceof CDOObjectWrapper)
         {
           CDOObjectWrapper wrapper = (CDOObjectWrapper)idOrObject;
-          Internal directResource = wrapper.eDirectResource();
+          Resource.Internal directResource = wrapper.eDirectResource();
           EObject container = wrapper.eContainer();
           if (!toBeDetached.contains(directResource) && !toBeDetached.contains(container))
           {
@@ -1378,7 +1591,7 @@
           else
           {
             InternalCDOObject detachedObject = (InternalCDOObject)detachedObjectEntry.getValue();
-            InternalCDORevision cleanRev = cleanRevisions.get(detachedObject);
+            InternalCDORevision cleanRev = getCleanRevision(detachedObject);
             cleanObject(detachedObject, cleanRev);
           }
         }
@@ -1395,7 +1608,7 @@
           // they were already reset to TRANSIENT earlier in this method
           if (!reattachedObjectsMap.values().contains(internalDirtyObject))
           {
-            CDOStateMachine.INSTANCE.rollback(internalDirtyObject);
+            CDOStateMachine2.INSTANCE.rollback(internalDirtyObject);
           }
         }
       }
@@ -1487,63 +1700,70 @@
       }
     }
 
-    dirty = savepoint.wasDirty();
+    IListener[] listeners = updateDirtyState(false);
+    if (listeners != null)
+    {
+      fireEvent(new FinishedEvent(Reason.ROLLED_BACK, null), listeners);
+    }
   }
 
   /**
    * @since 2.0
    */
+  @Deprecated
   public synchronized void detachObject(InternalCDOObject object)
   {
-    CDOTransactionHandler1[] handlers = getTransactionHandlers1();
-    for (int i = 0; i < handlers.length; i++)
+    throw new UnsupportedOperationException();
+
+    // CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    // for (int i = 0; i < handlers.length; i++)
+    // {
+    // CDOTransactionHandler1 handler = handlers[i];
+    // handler.detachingObject(this, object);
+    // }
+    //
+    // if (lastSavepoint.detachObject(object))
+    // {
+    // deregisterObject(object);
+    // }
+    //
+    // if (!dirty)
+    // {
+    // dirty = true;
+    // IListener[] listeners = getListeners();
+    // if (listeners != null)
+    // {
+    // fireEvent(new StartedEvent(), listeners);
+    // }
+    // }
+  }
+
+  public String dumpSavepoint()
+  {
+    int xxx;
+
+    StringBuilder builder = new StringBuilder();
+    dumpSavepoint(builder, "Dirty", dirty);
+    dumpSavepoint(builder, "Objects", getObjects().size());
+
+    Map<CDOID, ChangeInfo> changeInfos = lastSavepoint.getChangeInfos();
+    List<CDOID> ids = new ArrayList<CDOID>(changeInfos.keySet());
+    Collections.sort(ids);
+
+    for (CDOID id : ids)
     {
-      CDOTransactionHandler1 handler = handlers[i];
-      handler.detachingObject(this, object);
+      dumpSavepoint(builder, id.toString(), changeInfos.get(id));
     }
 
-    // deregister object
-    CDOID id = object.cdoID();
-    if (object.cdoState() == CDOState.NEW)
-    {
-      Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
+    return builder.toString();
+  }
 
-      // Determine if we added object
-      if (map.containsKey(id))
-      {
-        map.remove(id);
-      }
-      else
-      {
-        lastSavepoint.getDetachedObjects().put(id, object);
-      }
-
-      // deregister object
-      deregisterObject(object);
-    }
-    else
-    {
-      if (!cleanRevisions.containsKey(object))
-      {
-        cleanRevisions.put(object, object.cdoRevision());
-      }
-
-      lastSavepoint.getDetachedObjects().put(id, object);
-
-      // Object may have been reattached previously, in which case it must
-      // here be removed from the collection of reattached objects
-      lastSavepoint.getReattachedObjects().remove(id);
-    }
-
-    if (!dirty)
-    {
-      dirty = true;
-      IListener[] listeners = getListeners();
-      if (listeners != null)
-      {
-        fireEvent(new StartedEvent(), listeners);
-      }
-    }
+  private void dumpSavepoint(StringBuilder builder, String property, Object value)
+  {
+    builder.append(property);
+    builder.append(" = ");
+    builder.append(value);
+    builder.append("\n");
   }
 
   /**
@@ -1644,7 +1864,7 @@
       IListener[] listeners = getListeners();
       if (listeners != null)
       {
-        fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.ROLLED_BACK, idMappings), listeners);
+        fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Reason.ROLLED_BACK, idMappings), listeners);
       }
 
       CDOTransactionHandler2[] handlers = getTransactionHandlers2();
@@ -1677,12 +1897,14 @@
    */
   public synchronized InternalCDOSavepoint handleSetSavepoint()
   {
-    addToBase(lastSavepoint.getNewObjects());
+    // addToBase(lastSavepoint.getNewObjects());
+    InternalCDOSavepoint previousSavepoint = lastSavepoint;
     lastSavepoint = createSavepoint(lastSavepoint);
+
     return lastSavepoint;
   }
 
-  private CDOSavepointImpl createSavepoint(InternalCDOSavepoint lastSavepoint)
+  protected CDOSavepointImpl createSavepoint(InternalCDOSavepoint lastSavepoint)
   {
     return new CDOSavepointImpl(this, lastSavepoint);
   }
@@ -1712,16 +1934,11 @@
     return "CDOTransaction"; //$NON-NLS-1$
   }
 
-  public synchronized void registerAttached(InternalCDOObject object, boolean isNew)
+  public synchronized void attachObject(InternalCDOObject object)
   {
     if (TRACER.isEnabled())
     {
-      TRACER.format("Registering new object {0}", object); //$NON-NLS-1$
-    }
-
-    if (isNew)
-    {
-      registerNewPackage(object.eClass().getEPackage());
+      TRACER.format("Attaching object {0}", object); //$NON-NLS-1$
     }
 
     CDOTransactionHandler1[] handlers = getTransactionHandlers1();
@@ -1731,10 +1948,38 @@
       handler.attachingObject(this, object);
     }
 
-    if (isNew)
-    {
-      registerNew(lastSavepoint.getNewObjects(), object);
-    }
+    registerNewPackage(object.eClass().getEPackage());
+    registerObject(object);
+  }
+
+  public synchronized void _registerAttached(InternalCDOObject object, boolean isNew)
+  {
+    throw new UnsupportedOperationException();
+
+    // if (TRACER.isEnabled())
+    // {
+    //      TRACER.format("Registering new object {0}", object); //$NON-NLS-1$
+    // }
+    //
+    // if (isNew)
+    // {
+    // registerNewPackage(object.eClass().getEPackage());
+    // }
+    //
+    // registerObject(object);
+    //
+    // CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    // for (int i = 0; i < handlers.length; i++)
+    // {
+    // CDOTransactionHandler1 handler = handlers[i];
+    // handler.attachingObject(this, object);
+    // }
+    //
+    // int xxx;
+    // // if (isNew)
+    // // {
+    // // registerNew(lastSavepoint.getNewObjects(), object);
+    // // }
   }
 
   private void registerNewPackage(EPackage ePackage)
@@ -1749,109 +1994,122 @@
   /**
    * Receives notification for new and dirty objects
    */
-  public synchronized void registerFeatureDelta(final InternalCDOObject object, final CDOFeatureDelta featureDelta)
+  public synchronized void _registerFeatureDelta(final InternalCDOObject object, final CDOFeatureDelta featureDelta)
   {
-    CDOID id = object.cdoID();
-    boolean needToSaveFeatureDelta = true;
+    throw new UnsupportedOperationException();
 
-    if (object.cdoState() == CDOState.NEW)
-    {
-      // Register Delta for new objects only if objectA doesn't belong to
-      // this savepoint
-      if (getLastSavepoint().getPreviousSavepoint() == null || featureDelta == null)
-      {
-        needToSaveFeatureDelta = false;
-      }
-      else
-      {
-        Map<CDOID, CDOObject> map = getLastSavepoint().getNewObjects();
-        needToSaveFeatureDelta = !map.containsKey(id);
-      }
-    }
-
-    if (needToSaveFeatureDelta)
-    {
-      final InternalCDORevision revision = object.cdoRevision();
-
-      CDORevisionDelta revisionDelta = lastSavepoint.getRevisionDeltas2().get(id);
-      if (revisionDelta == null)
-      {
-        revisionDelta = CDORevisionUtil.createDelta(revision);
-        lastSavepoint.getRevisionDeltas2().put(id, revisionDelta);
-      }
-
-      ((InternalCDORevisionDelta)revisionDelta).addFeatureDelta(featureDelta, new CDOOriginSizeProvider()
-      {
-        public int getOriginSize()
-        {
-          EStructuralFeature feature = featureDelta.getFeature();
-          InternalCDORevision cleanRevision = cleanRevisions.get(object);
-          if (cleanRevision == null)
-          {
-            // Clean revision has *not yet* been registered, in this case the object revision *is still clean*
-            cleanRevision = revision;
-          }
-
-          CDOList list = cleanRevision.getList(feature);
-          return list.size();
-        }
-      });
-    }
-
-    CDOTransactionHandler1[] handlers = getTransactionHandlers1();
-    for (int i = 0; i < handlers.length; i++)
-    {
-      CDOTransactionHandler1 handler = handlers[i];
-      handler.modifyingObject(this, object, featureDelta);
-    }
+    // CDOID id = object.cdoID();
+    // boolean needToSaveFeatureDelta = true;
+    //
+    // if (object.cdoState() == CDOState.NEW)
+    // {
+    // // Register Delta for new objects only if objectA doesn't belong to
+    // // this savepoint
+    // if (lastSavepoint.getPreviousSavepoint() == null || featureDelta == null)
+    // {
+    // needToSaveFeatureDelta = false;
+    // }
+    // else
+    // {
+    // Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
+    // needToSaveFeatureDelta = !map.containsKey(id);
+    // }
+    // }
+    //
+    // if (needToSaveFeatureDelta)
+    // {
+    // final InternalCDORevision revision = object.cdoRevision();
+    //
+    // CDORevisionDelta revisionDelta = lastSavepoint.getRevisionDeltas2().get(id);
+    // if (revisionDelta == null)
+    // {
+    // revisionDelta = CDORevisionUtil.createDelta(revision);
+    // lastSavepoint.getRevisionDeltas2().put(id, revisionDelta);
+    // }
+    //
+    // ((InternalCDORevisionDelta)revisionDelta).addFeatureDelta(featureDelta, new CDOOriginSizeProvider()
+    // {
+    // public int getOriginSize()
+    // {
+    // EStructuralFeature feature = featureDelta.getFeature();
+    // InternalCDORevision cleanRevision = _cleanRevisions.get(object);
+    // if (cleanRevision == null)
+    // {
+    // // Clean revision has *not yet* been registered, in this case the object revision *is still clean*
+    // cleanRevision = revision;
+    // }
+    //
+    // CDOList list = cleanRevision.getList(feature);
+    // return list.size();
+    // }
+    // });
+    // }
+    //
+    // CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+    // for (int i = 0; i < handlers.length; i++)
+    // {
+    // CDOTransactionHandler1 handler = handlers[i];
+    // handler.modifyingObject(this, object, featureDelta);
+    // }
   }
 
-  public synchronized void registerRevisionDelta(CDORevisionDelta revisionDelta)
+  public synchronized void _registerRevisionDelta(CDORevisionDelta revisionDelta)
   {
-    Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
-    CDOID id = revisionDelta.getID();
-    if (!revisionDeltas.containsKey(id))
-    {
-      revisionDeltas.put(id, revisionDelta);
-    }
+    throw new UnsupportedOperationException();
+
+    // Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
+    // CDOID id = revisionDelta.getID();
+    // if (!revisionDeltas.containsKey(id))
+    // {
+    // revisionDeltas.put(id, revisionDelta);
+    // }
   }
 
-  public synchronized void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta)
+  public synchronized void _registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta)
   {
-    if (TRACER.isEnabled())
-    {
-      TRACER.format("Registering dirty object {0}", object); //$NON-NLS-1$
-    }
+    throw new UnsupportedOperationException();
 
-    if (featureDelta != null)
-    {
-      registerFeatureDelta(object, featureDelta);
-    }
-
-    registerNew(lastSavepoint.getDirtyObjects(), object);
+    // if (TRACER.isEnabled())
+    // {
+    //      TRACER.format("Registering dirty object {0}", object); //$NON-NLS-1$
+    // }
+    //
+    // if (featureDelta != null)
+    // {
+    // registerFeatureDelta(object, featureDelta);
+    // }
+    //
+    // int xxx;
+    // // registerNew(lastSavepoint.getDirtyObjects(), object);
+    // registerNew(null, object);
   }
 
   /**
    * TODO Simon: Should this method go to CDOSavePointImpl?
    */
   @SuppressWarnings({ "rawtypes", "unchecked" })
-  private void registerNew(Map map, InternalCDOObject object)
+  private void _registerNew(Map map, InternalCDOObject object)
   {
-    Object old = map.put(object.cdoID(), object);
-    if (old != null)
-    {
-      throw new IllegalStateException(MessageFormat.format(Messages.getString("CDOTransactionImpl.10"), object)); //$NON-NLS-1$
-    }
+    throw new UnsupportedOperationException();
 
-    if (!dirty)
-    {
-      dirty = true;
-      IListener[] listeners = getListeners();
-      if (listeners != null)
-      {
-        fireEvent(new StartedEvent(), listeners);
-      }
-    }
+    // if (map != null)
+    // {
+    // Object old = map.put(object.cdoID(), object);
+    // if (old != null)
+    // {
+    //        throw new IllegalStateException(MessageFormat.format(Messages.getString("CDOTransactionImpl.10"), object)); //$NON-NLS-1$
+    // }
+    // }
+    //
+    // if (!dirty)
+    // {
+    // dirty = true;
+    // IListener[] listeners = getListeners();
+    // if (listeners != null)
+    // {
+    // fireEvent(new StartedEvent(), listeners);
+    // }
+    // }
   }
 
   public synchronized List<CDOPackageUnit> analyzeNewPackages()
@@ -1923,45 +2181,51 @@
     return newPackages;
   }
 
-  private void cleanUp(CDOCommitContext commitContext)
+  private IListener[] cleanUp(CDOCommitContext commitContext, CommitTransactionResult result)
   {
-    if (commitContext == null || !commitContext.isPartialCommit())
+    if (commitContext != null)
     {
-      if (commitContext != null)
+      for (CDOObject object : commitContext.getDetachedObjects().values())
       {
-        for (CDOObject object : commitContext.getDetachedObjects().values())
-        {
-          cleanUpLockState(object);
-        }
+        cleanUpLockState(object);
+        lastSavepoint.removeChangeInfo(object);
       }
+    }
 
-      lastSavepoint = firstSavepoint;
-      firstSavepoint.clear();
-      firstSavepoint.setNextSavepoint(null);
+    firstSavepoint = lastSavepoint; // Last savepoint may contain left-overs from partial commit
+    lastSavepoint.setPreviousSavepoint(null);
+    conflict = 0; // TODO Left-overs from partial commit?
 
-      cleanRevisions.clear();
-      dirty = false;
-      conflict = 0;
-      idGenerator.reset();
+    if (commitContext != null && commitContext.isPartialCommit())
+    {
+      // collapseSavepoints(commitContext);
     }
     else
     {
-      collapseSavepoints(commitContext);
-
-      for (CDOObject object : commitContext.getDetachedObjects().values())
-      {
-        cleanRevisions.remove(object);
-        cleanUpLockState(object);
-      }
-
-      for (CDOObject object : commitContext.getDirtyObjects().values())
-      {
-        cleanRevisions.remove(object);
-      }
+      // lastSavepoint = firstSavepoint;
+      // firstSavepoint.clear();
+      // firstSavepoint.setNextSavepoint(null);
+      // conflict = 0;
+      idGenerator.reset();
     }
 
     // Reset partial-commit filter
     committables = null;
+
+    IListener[] listeners = updateDirtyState(false);
+    if (listeners != null)
+    {
+      if (result != null)
+      {
+        fireEvent(new FinishedEvent(Reason.COMMITTED, result.getIDMappings()), listeners);
+      }
+      else
+      {
+        fireEvent(new FinishedEvent(Reason.ROLLED_BACK, null), listeners);
+      }
+    }
+
+    return listeners;
   }
 
   private void cleanUpLockState(CDOObject object)
@@ -2156,7 +2420,8 @@
           {
             InternalCDOObject object = newInstance(revision);
             registerObject(object);
-            registerAttached(object, true);
+            int xxx;
+            // registerAttached(object, true);
 
             newObjects.add(object);
           }
@@ -2176,8 +2441,9 @@
           int oldVersion = object.cdoRevision().getVersion();
 
           merger.merge(object, delta);
-          registerRevisionDelta(delta);
-          registerDirty(object, null);
+          int xxx;
+          // registerRevisionDelta(delta);
+          // registerDirty(object, null);
 
           if (delta.getVersion() < oldVersion)
           {
@@ -2268,13 +2534,24 @@
     return lastSavepoint.getAllDetachedObjects();
   }
 
+  private CDOID getDetachedID(CDOObject object)
+  {
+    ChangeInfo changeInfo = lastSavepoint.getDetachedInfo(object);
+    if (changeInfo != null)
+    {
+      return changeInfo.getID();
+    }
+
+    return null;
+  }
+
   @Override
   protected synchronized CDOID getXRefTargetID(CDOObject target)
   {
-    CDORevisionKey key = cleanRevisions.get(target);
-    if (key != null)
+    CDOID detachedID = getDetachedID(target);
+    if (detachedID != null)
     {
-      return key.getID();
+      return detachedID;
     }
 
     return super.getXRefTargetID(target);
@@ -2301,12 +2578,11 @@
     }
 
     // The super implementation will return null for a transient (unattached) object;
-    // but in a tx, an transient object may previously have been attached. So we consult
-    // the cleanRevisions if that's the case.
-    CDORevisionKey revKey = cleanRevisions.get(object);
-    if (revKey != null && getDetachedObjects().containsValue(object))
+    // but in a tx, an transient object may previously have been attached.
+    CDOID detachedID = getDetachedID(object);
+    if (detachedID != null)
     {
-      id = revKey.getID();
+      id = detachedID;
     }
 
     return id;
@@ -2467,7 +2743,7 @@
           // doesn't apply: it must be removed for sure.)
           if (referencer.cdoState() == CDOState.DIRTY && referencerClassInfo.isPersistent(reference))
           {
-            InternalCDORevision cleanRevision = cleanRevisions.get(referencer);
+            InternalCDORevision cleanRevision = getCleanRevision(referencer);
 
             if (reference.isMany())
             {
@@ -2567,27 +2843,63 @@
     return committables;
   }
 
-  public synchronized Map<InternalCDOObject, InternalCDORevision> getCleanRevisions()
+  @Deprecated
+  public synchronized Map<InternalCDOObject, InternalCDORevision> _getCleanRevisions()
   {
-    return cleanRevisions;
+    throw new UnsupportedOperationException();
+
+    // new AbstractMap<InternalCDOObject, InternalCDORevision>()
+    // {
+    // @Override
+    // public Set<java.util.Map.Entry<InternalCDOObject, InternalCDORevision>> entrySet()
+    // {
+    // return new AbstractSet<java.util.Map.Entry<InternalCDOObject, InternalCDORevision>>()
+    // {
+    // @Override
+    // public Iterator<java.util.Map.Entry<InternalCDOObject, InternalCDORevision>> iterator()
+    // {
+    // return null;
+    // }
+    //
+    // @Override
+    // public int size()
+    // {
+    // return 0;
+    // }
+    // };
+    // }
+    // };
+    //
+    // return cleanRevisions;
+  }
+
+  public InternalCDORevision getCleanRevision(CDOObject object)
+  {
+    ChangeInfo changeInfo = lastSavepoint.getChangeInfos().get(object.cdoID());
+    if (changeInfo != null)
+    {
+      return changeInfo.getCleanRevision();
+    }
+
+    return null;
   }
 
   @Override
   protected InternalCDORevision getViewedRevision(InternalCDOObject object)
   {
-    InternalCDORevision rev = super.getViewedRevision(object);
+    InternalCDORevision revision = super.getViewedRevision(object);
 
     // Bug 336590: If we have a clean revision for this object, return that instead
-    if (rev != null)
+    if (revision != null)
     {
-      InternalCDORevision cleanRev = cleanRevisions.get(object);
-      if (cleanRev != null)
+      InternalCDORevision cleanRevision = getCleanRevision(object);
+      if (cleanRevision != null)
       {
-        return cleanRev;
+        return cleanRevision;
       }
     }
 
-    return rev;
+    return revision;
   }
 
   @Override
@@ -2595,7 +2907,7 @@
   {
     if (object.cdoState() == CDOState.TRANSIENT)
     {
-      InternalCDORevision revision = cleanRevisions.get(object);
+      InternalCDORevision revision = getCleanRevision(object);
       if (revision == null)
       {
         throw new IllegalStateException("No revision for transient object " + object);
@@ -2729,8 +3041,6 @@
    */
   private final class CDOCommitContextImpl implements InternalCDOCommitContext
   {
-    private InternalCDOTransaction transaction;
-
     /**
      * Tracks whether this commit is *actually* partial or not. (Having tx.committables != null does not in itself mean
      * that the commit will be partial, because the committables could cover all dirty/new/detached objects. But this
@@ -2752,104 +3062,87 @@
 
     private Map<ByteArrayWrapper, CDOLob<?>> lobs = new HashMap<ByteArrayWrapper, CDOLob<?>>();
 
-    public CDOCommitContextImpl(InternalCDOTransaction transaction)
+    public CDOCommitContextImpl()
     {
-      this.transaction = transaction;
       calculateCommitData();
     }
 
     private void calculateCommitData()
     {
+      newObjects = CDOIDUtil.createMap();
+      revisionDeltas = CDOIDUtil.createMap();
+      detachedObjects = CDOIDUtil.createMap();
+      dirtyObjects = CDOIDUtil.createMap();
+
       List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
 
-      newObjects = filterCommittables(transaction.getNewObjects());
-      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(newObjects.size());
-      for (CDOObject newObject : newObjects.values())
-      {
-        revisions.add(newObject.cdoRevision());
-      }
+      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>();
+      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>();
+      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>();
 
-      revisionDeltas = filterCommittables(transaction.getRevisionDeltas());
-      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(revisionDeltas.size());
-      for (CDORevisionDelta delta : revisionDeltas.values())
+      Collection<ChangeInfo> changeInfos = getLastSavepoint().getChangeInfos().values();
+      for (ChangeInfo changeInfo : changeInfos)
       {
-        deltas.add(delta);
-      }
+        InternalCDOObject object = changeInfo.getObject();
+        if (committables != null)
+        {
+          isPartialCommit = true;
+          if (!committables.contains(object))
+          {
+            continue;
+          }
+        }
 
-      detachedObjects = filterCommittables(transaction.getDetachedObjects());
-      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(detachedObjects.size());
-      for (Entry<CDOID, CDOObject> entry : detachedObjects.entrySet())
-      {
-        CDOObject object = entry.getValue();
-        InternalCDORevision cleanRevision = cleanRevisions.get(object);
-        if (cleanRevision == null)
+        CDOID id = changeInfo.getID();
+        switch (changeInfo.getType())
         {
-          // Can happen after merged detachments
-          CDORevision revision = object.cdoRevision();
-          detached.add(CDOIDUtil.createIDAndVersion(revision));
-        }
-        else if (cleanRevision.getBranch() == getBranch())
-        {
-          detached.add(CDOIDUtil.createIDAndVersion(cleanRevision));
-        }
-        else
-        {
-          detached.add(cleanRevision);
+        case NEW:
+          InternalCDORevision revision = object.cdoRevision();
+          revisions.add(revision);
+          newObjects.put(id, object);
+          break;
+
+        case DIRTY:
+          InternalCDORevisionDelta revisionDelta = changeInfo.getRevisionDelta();
+          deltas.add(revisionDelta);
+          revisionDeltas.put(changeInfo.getID(), revisionDelta);
+          dirtyObjects.put(id, object);
+          break;
+
+        case DETACHED:
+          InternalCDORevision key = changeInfo.getCleanRevision();
+          detached.add(key);
+          if (object != null)
+          {
+            detachedObjects.put(id, object);
+          }
         }
       }
 
-      dirtyObjects = filterCommittables(transaction.getDirtyObjects());
-
       CDOLockState[] locksOnNewObjectsArray = getLockStates(newObjects.keySet(), false);
       locksOnNewObjects = Arrays.asList(locksOnNewObjectsArray);
 
       commitData = CDOCommitInfoUtil.createCommitData(newPackageUnits, revisions, deltas, detached);
     }
 
-    private <T> Map<CDOID, T> filterCommittables(Map<CDOID, T> map)
-    {
-      if (committables == null)
-      {
-        // No partial commit filter -- nothing to do
-        return map;
-      }
-
-      Map<CDOID, T> newMap = CDOIDUtil.createMap();
-      for (Entry<CDOID, T> entry : map.entrySet())
-      {
-        CDOID id = entry.getKey();
-        CDOObject o = getObject(id);
-        if (committables.contains(o))
-        {
-          newMap.put(id, entry.getValue());
-        }
-        else
-        {
-          isPartialCommit = true;
-        }
-      }
-
-      return newMap;
-    }
-
     public String getUserID()
     {
-      return transaction.getSession().getUserID();
+      return getSession().getUserID();
     }
 
     public int getViewID()
     {
-      return transaction.getViewID();
+      return CDOTransactionImpl.this.getViewID();
     }
 
     public CDOBranch getBranch()
     {
-      return transaction.getBranch();
+      return CDOTransactionImpl.this.getBranch();
     }
 
     public InternalCDOTransaction getTransaction()
     {
-      return transaction;
+      return CDOTransactionImpl.this;
     }
 
     public boolean isPartialCommit()
@@ -2859,12 +3152,12 @@
 
     public boolean isAutoReleaseLocks()
     {
-      return transaction.options().isAutoReleaseLocksEnabled();
+      return options().isAutoReleaseLocksEnabled();
     }
 
     public String getCommitComment()
     {
-      return transaction.getCommitComment();
+      return CDOTransactionImpl.this.getCommitComment();
     }
 
     public CDOCommitData getCommitData()
@@ -2984,18 +3277,29 @@
       }
     }
 
+    private void preCommit(Map<CDOID, CDOObject> objects, Map<ByteArrayWrapper, CDOLob<?>> lobs)
+    {
+      if (!objects.isEmpty())
+      {
+        for (CDOObject object : objects.values())
+        {
+          collectLobs((InternalCDORevision)object.cdoRevision(), lobs);
+          ((InternalCDOObject)object).cdoInternalPreCommit();
+        }
+      }
+    }
+
     public void postCommit(CommitTransactionResult result)
     {
       try
       {
         InternalCDOSession session = getSession();
-        long timeStamp = result.getTimeStamp();
         boolean clearResourcePathCache = result.isClearResourcePathCache();
 
         if (result.getRollbackMessage() != null)
         {
-          CDOCommitInfo commitInfo = new FailureCommitInfo(timeStamp, result.getPreviousTimeStamp());
-          session.invalidate(commitInfo, transaction, clearResourcePathCache);
+          CDOCommitInfo commitInfo = new FailureCommitInfo(result.getTimeStamp(), result.getPreviousTimeStamp());
+          session.invalidate(commitInfo, CDOTransactionImpl.this, clearResourcePathCache);
           return;
         }
 
@@ -3012,31 +3316,22 @@
           ((InternalCDOPackageUnit)newPackageUnit).setState(CDOPackageUnit.State.LOADED);
         }
 
-        postCommit(getNewObjects(), result);
-        postCommit(getDirtyObjects(), result);
+        transitionObjects(getNewObjects(), result);
+        transitionObjects(getDirtyObjects(), result);
 
-        for (CDORevisionDelta delta : getRevisionDeltas().values())
-        {
-          ((InternalCDORevisionDelta)delta).adjustReferences(result.getReferenceAdjuster());
-        }
-
-        for (CDOID id : getDetachedObjects().keySet())
-        {
-          removeObject(id);
-        }
-
-        CDOCommitInfo commitInfo = makeCommitInfo(timeStamp, result.getPreviousTimeStamp());
+        CDOCommitInfo commitInfo = makeCommitInfo(result);
         if (!commitInfo.isEmpty())
         {
-          session.invalidate(commitInfo, transaction, clearResourcePathCache);
+          session.invalidate(commitInfo, CDOTransactionImpl.this, clearResourcePathCache);
         }
 
         // Bug 290032 - Sticky views
         if (session.isSticky())
         {
           CDOBranchPoint commitBranchPoint = CDOBranchUtil.copyBranchPoint(result);
-          for (CDOObject object : getNewObjects().values()) // Note: keyset() does not work because ID mappings are
-                                                            // not applied there!
+
+          // Note: keyset() does not work because ID mappings are not applied there!
+          for (CDOObject object : getNewObjects().values())
           {
             session.setCommittedSinceLastRefresh(object.cdoID(), commitBranchPoint);
           }
@@ -3059,29 +3354,21 @@
           if (handler instanceof CDOTransactionHandler3)
           {
             CDOTransactionHandler3 handler3 = (CDOTransactionHandler3)handler;
-            handler3.committedTransaction(transaction, this, commitInfo);
+            handler3.committedTransaction(CDOTransactionImpl.this, this, commitInfo);
           }
           else
           {
-            handler.committedTransaction(transaction, this);
+            handler.committedTransaction(CDOTransactionImpl.this, this);
           }
         }
 
-        getChangeSubscriptionManager().committedTransaction(transaction, this);
-        getAdapterManager().committedTransaction(transaction, this);
+        getChangeSubscriptionManager().committedTransaction(CDOTransactionImpl.this, this);
+        getAdapterManager().committedTransaction(CDOTransactionImpl.this, this);
 
-        cleanUp(this);
-
-        IListener[] listeners = getListeners();
-        if (listeners != null)
+        IListener[] listeners = cleanUp(this, result);
+        if (listeners != null && branchChanged)
         {
-          if (branchChanged)
-          {
-            fireViewTargetChangedEvent(oldBranch.getHead(), listeners);
-          }
-
-          Map<CDOID, CDOID> idMappings = result.getIDMappings();
-          fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.COMMITTED, idMappings), listeners);
+          fireViewTargetChangedEvent(oldBranch.getHead(), listeners);
         }
 
         CDOLockState[] newLockStates = result.getNewLockStates();
@@ -3100,29 +3387,69 @@
       }
     }
 
-    private CDOCommitInfo makeCommitInfo(long timeStamp, long previousTimeStamp)
-    {
-      InternalCDOSession session = getSession();
-      CDOBranch branch = getBranch();
-      String userID = session.getUserID();
-      String comment = getCommitComment();
-
-      InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
-      return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, commitData);
-    }
-
-    private void preCommit(Map<CDOID, CDOObject> objects, Map<ByteArrayWrapper, CDOLob<?>> lobs)
+    private void transitionObjects(Map<CDOID, CDOObject> objects, CommitTransactionResult result)
     {
       if (!objects.isEmpty())
       {
         for (CDOObject object : objects.values())
         {
-          collectLobs((InternalCDORevision)object.cdoRevision(), lobs);
-          ((InternalCDOObject)object).cdoInternalPreCommit();
+          CDOStateMachine2.INSTANCE.commit((InternalCDOObject)object, result);
         }
       }
     }
 
+    private CDOCommitInfo makeCommitInfo(CommitTransactionResult result)
+    {
+      final CDOReferenceAdjuster defaultReferenceAdjuster = result.getReferenceAdjuster();
+      CDOReferenceAdjuster referenceAdjuster = new CDOReferenceAdjuster()
+      {
+        public Object adjustReference(Object idOrObject, EStructuralFeature feature, int index)
+        {
+          if (idOrObject == null || idOrObject == CDOID.NULL)
+          {
+            return idOrObject;
+          }
+
+          // Adjust remaining CDOIDs to detached objects
+          if (idOrObject instanceof CDOID)
+          {
+            ChangeInfo changeInfo = lastSavepoint.getChangeInfo((CDOID)idOrObject);
+            if (changeInfo != null && changeInfo.getType() == ChangeType.DETACHED)
+            {
+              return changeInfo.getObject();
+            }
+          }
+
+          // Return detached objects to avoid dangling reference exceptions below
+          if (idOrObject instanceof InternalCDOObject)
+          {
+            ChangeInfo changeInfo = lastSavepoint.getDetachedInfo((InternalCDOObject)idOrObject);
+            if (changeInfo != null)
+            {
+              return changeInfo.getObject();
+            }
+          }
+
+          return defaultReferenceAdjuster.adjustReference(idOrObject, feature, index);
+        }
+      };
+
+      for (CDORevisionDelta delta : getRevisionDeltas().values())
+      {
+        ((InternalCDORevisionDelta)delta).adjustReferences(referenceAdjuster);
+      }
+
+      InternalCDOSession session = getSession();
+      CDOBranch branch = getBranch();
+      String userID = session.getUserID();
+      String comment = getCommitComment();
+      long timeStamp = result.getTimeStamp();
+      long previousTimeStamp = result.getPreviousTimeStamp();
+
+      InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
+      return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, commitData);
+    }
+
     private void collectLobs(InternalCDORevision revision, Map<ByteArrayWrapper, CDOLob<?>> lobs)
     {
       EStructuralFeature[] features = revision.getClassInfo().getAllPersistentFeatures();
@@ -3139,17 +3466,6 @@
         }
       }
     }
-
-    private void postCommit(Map<CDOID, CDOObject> objects, CommitTransactionResult result)
-    {
-      if (!objects.isEmpty())
-      {
-        for (CDOObject object : objects.values())
-        {
-          CDOStateMachine.INSTANCE.commit((InternalCDOObject)object, result);
-        }
-      }
-    }
   }
 
   /**
@@ -3177,19 +3493,32 @@
   {
     private static final long serialVersionUID = 1L;
 
-    private Type type;
+    private Reason reason;
 
     private Map<CDOID, CDOID> idMappings;
 
-    private FinishedEvent(Type type, Map<CDOID, CDOID> idMappings)
+    private FinishedEvent(Reason type, Map<CDOID, CDOID> idMappings)
     {
-      this.type = type;
+      reason = type;
       this.idMappings = idMappings;
     }
 
+    @Deprecated
     public Type getType()
     {
-      return type;
+      switch (reason)
+      {
+      case COMMITTED:
+        return Type.COMMITTED;
+
+      default:
+        return Type.ROLLED_BACK;
+      }
+    }
+
+    public Reason getReason()
+    {
+      return reason;
     }
 
     public Map<CDOID, CDOID> getIDMappings()
@@ -3200,8 +3529,13 @@
     @Override
     public String toString()
     {
-      return MessageFormat.format("CDOTransactionFinishedEvent[source={0}, type={1}, idMappings={2}]", getSource(), //$NON-NLS-1$
-          getType(), idMappings == null ? 0 : idMappings.size());
+      String prefix = "CDOTransactionFinishedEvent[source={0}, reason={1}";
+      if (idMappings == null)
+      {
+        return MessageFormat.format(prefix + "]", getSource(), getReason());
+      }
+
+      return MessageFormat.format(prefix + ", idMappings={2}]", getSource(), getReason(), idMappings.size());
     }
   }
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
index 38658f4..4d55407 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
@@ -139,7 +139,7 @@
   {
     // Getting the deltas from the TX is not a good idea...
     // We better recompute a fresh delta:
-    InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+    InternalCDORevision cleanRev = transaction.getCleanRevision(dirtyObject);
     CheckUtil.checkNull(cleanRev, "Could not obtain clean revision for dirty object " + dirtyObject);
 
     InternalCDOClassInfo classInfo = dirtyObject.cdoClassInfo();
@@ -275,7 +275,7 @@
       else if (featureDelta instanceof CDOClearFeatureDelta)
       {
         EStructuralFeature feat = ((CDOClearFeatureDelta)featureDelta).getFeature();
-        InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+        InternalCDORevision cleanRev = transaction.getCleanRevision(dirtyObject);
         int n = cleanRev.size(feat);
         for (int i = 0; i < n; i++)
         {
@@ -287,7 +287,7 @@
       else if (featureDelta instanceof CDOUnsetFeatureDelta)
       {
         EStructuralFeature feat = ((CDOUnsetFeatureDelta)featureDelta).getFeature();
-        InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+        InternalCDORevision cleanRev = transaction.getCleanRevision(dirtyObject);
         Object idOrObject = cleanRev.getValue(feat);
         CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
         checkIncluded(id, "removed child / refTarget of", dirtyObject);
@@ -436,7 +436,7 @@
     // that we can find the pre-detach revision in tx.getFormerRevisions(). However,
     // the object may have already been dirty prior to detachment, so we check the
     // clean revisions first.
-    InternalCDORevision cleanRev = transaction.getCleanRevisions().get(referencer);
+    InternalCDORevision cleanRev = transaction.getCleanRevision(referencer);
     CheckUtil.checkState(cleanRev, "cleanRev");
 
     InternalCDOClassInfo referencerClassInfo = ((InternalCDOObject)referencer).cdoClassInfo();
@@ -491,7 +491,7 @@
 
   private void checkFormerContainerIncluded(CDOObject detachedObject) throws CommitIntegrityException
   {
-    InternalCDORevision rev = transaction.getCleanRevisions().get(detachedObject);
+    InternalCDORevision rev = transaction.getCleanRevision(detachedObject);
     CheckUtil.checkNull(rev, "Could not obtain clean revision for detached object " + detachedObject);
 
     CDOID id = getContainerOrResourceID(rev);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java
index 4c11427..ad90c58 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java
@@ -37,6 +37,7 @@
 import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
 import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
 import org.eclipse.emf.cdo.common.util.CDOException;
+import org.eclipse.emf.cdo.common.util.PartialCollectionLoadingNotSupportedException;
 import org.eclipse.emf.cdo.eresource.CDOBinaryResource;
 import org.eclipse.emf.cdo.eresource.CDOResource;
 import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
@@ -66,6 +67,7 @@
 import org.eclipse.emf.internal.cdo.bundle.OM;
 import org.eclipse.emf.internal.cdo.messages.Messages;
 import org.eclipse.emf.internal.cdo.object.CDOLegacyAdapter;
+import org.eclipse.emf.internal.cdo.object.CDONotificationBuilder;
 import org.eclipse.emf.internal.cdo.query.CDOQueryImpl;
 import org.eclipse.emf.internal.cdo.transaction.CDOTransactionImpl;
 
@@ -90,6 +92,7 @@
 
 import org.eclipse.emf.common.notify.Adapter;
 import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.NotificationChain;
 import org.eclipse.emf.common.notify.impl.AdapterImpl;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.URI;
@@ -111,7 +114,6 @@
 
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -1131,10 +1133,35 @@
     }
 
     cleanObject(object, revision);
-    CDOStateMachine.INSTANCE.dispatchLoadNotification(object);
+    dispatchLoadNotification(object);
     return object;
   }
 
+  public void dispatchLoadNotification(InternalCDOObject object)
+  {
+    if (options().isLoadNotificationEnabled())
+    {
+      try
+      {
+        InternalCDORevision revision = object.cdoRevision();
+        CDONotificationBuilder builder = new CDONotificationBuilder(this);
+
+        NotificationChain notification = builder.buildNotification(object, revision);
+        if (notification != null)
+        {
+          notification.dispatch();
+        }
+      }
+      catch (PartialCollectionLoadingNotSupportedException ex)
+      {
+        if (TRACER.isEnabled())
+        {
+          TRACER.trace(ex);
+        }
+      }
+    }
+  }
+
   private CDOResource newResourceInstance(InternalCDORevision revision)
   {
     String path = getResourcePath(revision);
@@ -1510,7 +1537,7 @@
         Pair<CDORevision, CDORevisionDelta> oldInfo = Pair.create(changedObject.cdoRevision(), delta);
         // if (!isLocked(changedObject))
         {
-          CDOStateMachine.INSTANCE.invalidate((InternalCDOObject)changedObject, key);
+          CDOStateMachine2.INSTANCE.invalidate((InternalCDOObject)changedObject, key);
         }
 
         revisionDeltas.put(changedObject, delta);
@@ -1535,7 +1562,7 @@
             CDORevisionDelta.DETACHED);
         // if (!isLocked(detachedObject))
         {
-          CDOStateMachine.INSTANCE.detachRemote(detachedObject);
+          CDOStateMachine2.INSTANCE.detachRemote(detachedObject);
         }
 
         detachedObjects.add(detachedObject);
@@ -1590,30 +1617,31 @@
   @Deprecated
   public synchronized int reload(CDOObject... objects)
   {
-    Collection<InternalCDOObject> internalObjects;
-    if (objects != null && objects.length != 0)
-    {
-      internalObjects = new ArrayList<InternalCDOObject>(objects.length);
-      for (CDOObject object : objects)
-      {
-        if (object instanceof InternalCDOObject)
-        {
-          internalObjects.add((InternalCDOObject)object);
-        }
-      }
-    }
-    else
-    {
-      internalObjects = new ArrayList<InternalCDOObject>(this.objects.values());
-    }
-
-    int result = internalObjects.size();
-    if (result != 0)
-    {
-      CDOStateMachine.INSTANCE.reload(internalObjects.toArray(new InternalCDOObject[result]));
-    }
-
-    return result;
+    // Collection<InternalCDOObject> internalObjects;
+    // if (objects != null && objects.length != 0)
+    // {
+    // internalObjects = new ArrayList<InternalCDOObject>(objects.length);
+    // for (CDOObject object : objects)
+    // {
+    // if (object instanceof InternalCDOObject)
+    // {
+    // internalObjects.add((InternalCDOObject)object);
+    // }
+    // }
+    // }
+    // else
+    // {
+    // internalObjects = new ArrayList<InternalCDOObject>(this.objects.values());
+    // }
+    //
+    // int result = internalObjects.size();
+    // if (result != 0)
+    // {
+    // CDOStateMachine2.INSTANCE.reload(internalObjects.toArray(new InternalCDOObject[result]));
+    // }
+    //
+    // return result;
+    return 0;
   }
 
   public void close()
@@ -1733,7 +1761,7 @@
 
   protected InternalCDORevision getViewedRevision(InternalCDOObject object)
   {
-    return CDOStateMachine.INSTANCE.readNoLoad(object);
+    return CDOStateMachine2.INSTANCE.readNoLoad(object);
   }
 
   public synchronized CDOChangeSetData compareRevisions(CDOBranchPoint source)
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
index 22d87db..8fc8e81 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
@@ -101,17 +101,6 @@
     init(CDOState.TRANSIENT, CDOEvent.COMMIT, FAIL);
     init(CDOState.TRANSIENT, CDOEvent.ROLLBACK, FAIL);
 
-    init(CDOState.PREPARED, CDOEvent.PREPARE, FAIL);
-    init(CDOState.PREPARED, CDOEvent.ATTACH, new AttachTransition());
-    init(CDOState.PREPARED, CDOEvent.DETACH, FAIL);
-    init(CDOState.PREPARED, CDOEvent.REATTACH, FAIL);
-    init(CDOState.PREPARED, CDOEvent.READ, IGNORE);
-    init(CDOState.PREPARED, CDOEvent.WRITE, FAIL);
-    init(CDOState.PREPARED, CDOEvent.INVALIDATE, FAIL);
-    init(CDOState.PREPARED, CDOEvent.DETACH_REMOTE, FAIL);
-    init(CDOState.PREPARED, CDOEvent.COMMIT, FAIL);
-    init(CDOState.PREPARED, CDOEvent.ROLLBACK, FAIL);
-
     init(CDOState.NEW, CDOEvent.PREPARE, FAIL);
     init(CDOState.NEW, CDOEvent.ATTACH, FAIL);
     init(CDOState.NEW, CDOEvent.DETACH, new DetachTransition());
@@ -191,11 +180,11 @@
   }
 
   /**
-   * The object is already attached in EMF world. It contains all the information needed to know where it will be
-   * connected.
-   *
-   * @since 2.0
-   */
+  * The object is already attached in EMF world. It contains all the information needed to know where it will be
+  * connected.
+  *
+  * @since 2.0
+  */
   public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
   {
     synchronized (transaction)
@@ -211,24 +200,9 @@
     }
   }
 
-  private void attachOrReattach(InternalCDOObject object, InternalCDOTransaction transaction)
-  {
-    // Bug 283985 (Re-attachment):
-    // If the object going through a prepareTransition is present in cleanRevisions,
-    // then it was detached earlier, and so we can infer that it is being re-attached
-    if (transaction.getCleanRevisions().containsKey(object))
-    {
-      reattachObject(object, transaction);
-    }
-    else
-    {
-      attachObject(object);
-    }
-  }
-
   /**
-   * Phase 1: TRANSIENT --> PREPARED
-   */
+  * Phase 1: TRANSIENT --> PREPARED
+  */
   private void prepare(InternalCDOObject object,
       Pair<InternalCDOTransaction, List<InternalCDOObject>> transactionAndContents)
   {
@@ -240,32 +214,34 @@
     process(object, CDOEvent.PREPARE, transactionAndContents);
   }
 
-  /**
-   * Phase 2: PREPARED --> NEW
-   */
-  private void attachObject(InternalCDOObject object)
+  private void attachOrReattach(InternalCDOObject object, InternalCDOTransaction transaction)
   {
-    if (TRACER.isEnabled())
+    // Bug 283985 (Re-attachment):
+    // If the object going through a prepareTransition is present in cleanRevisions,
+    // then it was detached earlier, and so we can infer that it is being re-attached
+    if (transaction.getCleanRevision(object) != null)
     {
-      TRACER.format("ATTACH: {0}", object); //$NON-NLS-1$
+      if (TRACER.isEnabled())
+      {
+        TRACER.format("REATTACH: {0}", object);
+      }
+
+      process(object, CDOEvent.REATTACH, transaction);
     }
-
-    process(object, CDOEvent.ATTACH, null);
-  }
-
-  private void reattachObject(InternalCDOObject object, InternalCDOTransaction transaction)
-  {
-    if (TRACER.isEnabled())
+    else
     {
-      TRACER.format("REATTACH: {0}", object);
-    }
+      if (TRACER.isEnabled())
+      {
+        TRACER.format("ATTACH: {0}", object); //$NON-NLS-1$
+      }
 
-    process(object, CDOEvent.REATTACH, transaction);
+      process(object, CDOEvent.ATTACH, null);
+    }
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void detach(InternalCDOObject object)
   {
     synchronized (getMonitor(object))
@@ -303,8 +279,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public InternalCDORevision read(InternalCDOObject object)
   {
     synchronized (getMonitor(object))
@@ -321,8 +297,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public InternalCDORevision readNoLoad(InternalCDOObject object)
   {
     synchronized (getMonitor(object))
@@ -344,16 +320,16 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void write(InternalCDOObject object)
   {
     write(object, null);
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void write(InternalCDOObject object, CDOFeatureDelta featureDelta)
   {
     synchronized (getMonitor(object))
@@ -373,8 +349,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void reload(InternalCDOObject... objects)
   {
     if (objects == null || objects.length == 0)
@@ -398,8 +374,8 @@
   }
 
   /**
-   * @since 3.0
-   */
+  * @since 3.0
+  */
   public void invalidate(InternalCDOObject object, CDORevisionKey key)
   {
     synchronized (getMonitor(object))
@@ -414,8 +390,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void detachRemote(InternalCDOObject object)
   {
     synchronized (getMonitor(object))
@@ -430,8 +406,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void commit(InternalCDOObject object, CommitTransactionResult result)
   {
     synchronized (getMonitor(object))
@@ -446,8 +422,8 @@
   }
 
   /**
-   * @since 2.0
-   */
+  * @since 2.0
+  */
   public void rollback(InternalCDOObject object)
   {
     synchronized (getMonitor(object))
@@ -486,8 +462,8 @@
   }
 
   /**
-   * Removes clutter from the trace log
-   */
+  * Removes clutter from the trace log
+  */
   private void trace(InternalCDOObject object, CDOEvent event)
   {
     CDOState state = object.cdoState();
@@ -509,7 +485,7 @@
   public void internalReattach(InternalCDOObject object, InternalCDOTransaction transaction)
   {
     InternalCDORevisionManager revisionManager = transaction.getSession().getRevisionManager();
-    InternalCDORevision cleanRevision = transaction.getCleanRevisions().get(object).copy();
+    InternalCDORevision cleanRevision = transaction.getCleanRevision(object).copy();
     CDOID id = cleanRevision.getID();
 
     // Bug 373096: Determine clean revision of the CURRENT/LAST savepoint
@@ -546,8 +522,8 @@
     }
     else
     {
-      transaction.registerRevisionDelta(revisionDelta);
-      transaction.registerDirty(object, (CDOFeatureDelta)null);
+      // transaction.registerRevisionDelta(revisionDelta);
+      // transaction.registerDirty(object, (CDOFeatureDelta)null);
       changeState(object, CDOState.DIRTY);
     }
 
@@ -583,20 +559,20 @@
   }
 
   /**
-   * Prepares a tree of transient objects to be subsequently {@link AttachTransition attached} to a CDOView.
-   * <p>
-   * Execution is recursive and includes:
-   * <ol>
-   * <li>Assignment of a new {@link CDOIDTemp}
-   * <li>Assignment of a new {@link CDORevision}
-   * <li>Bidirectional association with the {@link CDOView}
-   * <li>Registration with the {@link CDOTransaction}
-   * <li>Changing state to {@link CDOState#PREPARED PREPARED}
-   * </ol>
-   *
-   * @see AttachTransition
-   * @author Eike Stepper
-   */
+  * Prepares a tree of transient objects to be subsequently {@link AttachTransition attached} to a CDOView.
+  * <p>
+  * Execution is recursive and includes:
+  * <ol>
+  * <li>Assignment of a new {@link CDOIDTemp}
+  * <li>Assignment of a new {@link CDORevision}
+  * <li>Bidirectional association with the {@link CDOView}
+  * <li>Registration with the {@link CDOTransaction}
+  * <li>Changing state to {@link CDOState#PREPARED PREPARED}
+  * </ol>
+  *
+  * @see AttachTransition
+  * @author Eike Stepper
+  */
   private final class PrepareTransition implements
       ITransition<CDOState, CDOEvent, InternalCDOObject, Pair<InternalCDOTransaction, List<InternalCDOObject>>>
   {
@@ -608,10 +584,12 @@
 
       // If the object going through a prepareTransition is present in cleanRevisions,
       // then it was detached earlier, and so we can infer that it is being re-attached
-      boolean reattaching = transaction.getCleanRevisions().containsKey(object);
+      boolean reattaching = transaction.getCleanRevision(object) != null;
 
       if (!reattaching)
       {
+        // TRANSIENT
+
         // Prepare object
         CDOID id = transaction.createIDForNewObject(object.cdoInternalInstance());
         object.cdoInternalSetView(transaction);
@@ -633,7 +611,7 @@
         transaction.registerObject(object);
       }
 
-      transaction.registerAttached(object, !reattaching);
+      // transaction.registerAttached(object, !reattaching);
 
       // Prepare content tree
       for (Iterator<InternalEObject> it = getPersistentContents(object); it.hasNext();)
@@ -680,23 +658,23 @@
   }
 
   /**
-   * Attaches a tree of {@link PrepareTransition prepared} objects to a CDOView.
-   * <p>
-   * Execution is recursive and includes:
-   * <ol>
-   * <li>Calling {@link InternalCDOObject#cdoInternalPostAttach()},<br>
-   * which includes for {@link CDOObjectImpl}:
-   * <ol>
-   * <li>Population of the CDORevision with the current values in
-   * {@link EStoreEObjectImpl#eSetting(org.eclipse.emf.ecore.EStructuralFeature) eSettings}
-   * <li>Unsetting {@link EStoreEObjectImpl#eSetting(org.eclipse.emf.ecore.EStructuralFeature) eSettings}
-   * </ol>
-   * <li>Changing state to {@link CDOState#NEW NEW}
-   * </ol>
-   *
-   * @see PrepareTransition
-   * @author Eike Stepper
-   */
+  * Attaches a tree of {@link PrepareTransition prepared} objects to a CDOView.
+  * <p>
+  * Execution is recursive and includes:
+  * <ol>
+  * <li>Calling {@link InternalCDOObject#cdoInternalPostAttach()},<br>
+  * which includes for {@link CDOObjectImpl}:
+  * <ol>
+  * <li>Population of the CDORevision with the current values in
+  * {@link EStoreEObjectImpl#eSetting(org.eclipse.emf.ecore.EStructuralFeature) eSettings}
+  * <li>Unsetting {@link EStoreEObjectImpl#eSetting(org.eclipse.emf.ecore.EStructuralFeature) eSettings}
+  * </ol>
+  * <li>Changing state to {@link CDOState#NEW NEW}
+  * </ol>
+  *
+  * @see PrepareTransition
+  * @author Eike Stepper
+  */
   private final class AttachTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object NULL)
@@ -707,10 +685,10 @@
   }
 
   /**
-   * Bug 283985 (Re-attachment)
-   *
-   * @author Caspar De Groot
-   */
+  * Bug 283985 (Re-attachment)
+  *
+  * @author Caspar De Groot
+  */
   private final class ReattachTransition implements
       ITransition<CDOState, CDOEvent, InternalCDOObject, InternalCDOTransaction>
   {
@@ -806,8 +784,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   private static final class DetachTransition implements
       ITransition<CDOState, CDOEvent, InternalCDOObject, List<InternalCDOObject>>
   {
@@ -836,8 +814,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   final private class CommitTransition implements
       ITransition<CDOState, CDOEvent, InternalCDOObject, CommitTransactionResult>
   {
@@ -873,8 +851,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   private final class RollbackTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object NULL)
@@ -896,8 +874,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   private final class WriteTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
@@ -909,20 +887,20 @@
         throw new NoPermissionException(cleanRevision);
       }
 
-      transaction.getCleanRevisions().put(object, cleanRevision);
+      // transaction.getCleanRevisions().put(object, cleanRevision);
 
       // Copy revision
       InternalCDORevision revision = object.cdoRevision().copy();
       object.cdoInternalSetRevision(revision);
 
-      transaction.registerDirty(object, (CDOFeatureDelta)featureDelta);
+      // transaction.registerDirty(object, (CDOFeatureDelta)featureDelta);
       changeState(object, CDOState.DIRTY);
     }
   }
 
   /**
-   * @author Simon McDuff
-   */
+  * @author Simon McDuff
+  */
   private static final class WriteNewTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
@@ -934,13 +912,13 @@
       }
 
       InternalCDOTransaction transaction = object.cdoView().toTransaction();
-      transaction.registerFeatureDelta(object, (CDOFeatureDelta)featureDelta);
+      // transaction.registerFeatureDelta(object, (CDOFeatureDelta)featureDelta);
     }
   }
 
   /**
-   * @author Simon McDuff
-   */
+  * @author Simon McDuff
+  */
   private static final class RewriteTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
@@ -952,20 +930,20 @@
       }
 
       InternalCDOTransaction transaction = object.cdoView().toTransaction();
-      transaction.registerFeatureDelta(object, (CDOFeatureDelta)featureDelta);
+      // transaction.registerFeatureDelta(object, (CDOFeatureDelta)featureDelta);
     }
   }
 
   /**
-   * @author Simon McDuff
-   */
+  * @author Simon McDuff
+  */
   private static class DetachRemoteTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     static final DetachRemoteTransition INSTANCE = new DetachRemoteTransition();
 
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object NULL)
     {
-      CDOStateMachine.INSTANCE.changeState(object, CDOState.INVALID);
+      // INSTANCE.changeState(object, CDOState.INVALID);
 
       InternalCDOView view = object.cdoView();
       view.deregisterObject(object);
@@ -974,8 +952,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   private class InvalidateTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, CDORevisionKey>
   {
     public void execute(InternalCDOObject object, CDOState state, CDOEvent event, CDORevisionKey key)
@@ -1054,9 +1032,9 @@
   }
 
   /**
-   * @author Eike Stepper
-   * @since 2.0
-   */
+  * @author Eike Stepper
+  * @since 2.0
+  */
   private class ConflictTransition extends InvalidateTransition
   {
     @Override
@@ -1073,8 +1051,8 @@
   }
 
   /**
-   * @author Simon McDuff
-   */
+  * @author Simon McDuff
+  */
   private final class InvalidConflictTransition extends ConflictTransition
   {
     @Override
@@ -1088,8 +1066,8 @@
   }
 
   /**
-   * @author Eike Stepper
-   */
+  * @author Eike Stepper
+  */
   private final class LoadTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     private boolean forWrite;
@@ -1130,8 +1108,8 @@
   }
 
   /**
-   * @author Simon McDuff
-   */
+  * @author Simon McDuff
+  */
   private static final class InvalidTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
   {
     public static final InvalidTransition INSTANCE = new InvalidTransition();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java
new file mode 100644
index 0000000..a9a700a
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine2.java
@@ -0,0 +1,1455 @@
+/*
+ * Copyright (c) 2011-2013 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
+ *    Simon McDuff - maintenance
+ */
+package org.eclipse.emf.internal.cdo.view;
+
+import org.eclipse.emf.cdo.CDOState;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevisable;
+import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOOriginSizeProvider;
+import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.security.NoPermissionException;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.view.CDOInvalidationPolicy;
+
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.Clean;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.Conflict;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.Dirty;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.New;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.Proxy;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Attached.Undone;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Unattached.Detached;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Unattached.Transient;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Unusable.Invalid;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2.Unusable.Invalid_Conflict;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EContentsEList;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
+import org.eclipse.emf.spi.cdo.FSMUtil;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDOSavepoint;
+import org.eclipse.emf.spi.cdo.InternalCDOSavepoint.ChangeInfo;
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
+import org.eclipse.emf.spi.cdo.InternalCDOView;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ */
+public final class CDOStateMachine2
+{
+  public static final CDOStateMachine2 INSTANCE = new CDOStateMachine2();
+
+  private static final State TRANSIENT = new Transient();
+
+  private static final State CLEAN = new Clean();
+
+  private static final State PROXY = new Proxy();
+
+  private static final State CONFLICT = new Conflict();
+
+  private static final State INVALID = new Invalid();
+
+  private static final State INVALID_CONFLICT = new Invalid_Conflict();
+
+  static final ThreadLocal<Boolean> SWITCHING_TARGET = new InheritableThreadLocal<Boolean>();
+
+  private CDOStateMachine2()
+  {
+  }
+
+  public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
+  {
+    synchronized (transaction)
+    {
+      State state = getState(object, transaction, false);
+      traceEvent("Attach", object, state);
+      state.attach(object, transaction);
+    }
+  }
+
+  public void detach(InternalCDOObject object)
+  {
+    synchronized (getMonitor(object))
+    {
+      // Accumulate objects that need to be detached.
+      // In case of errors, we will keep the graph exactly like it was before.
+      List<Runnable> runnables = new ArrayList<Runnable>();
+      detach(object, runnables);
+
+      for (Runnable runnable : runnables)
+      {
+        runnable.run();
+      }
+    }
+  }
+
+  private void detach(InternalCDOObject object, List<Runnable> runnables)
+  {
+    State state = getState(object, null, false);
+    traceEvent("Detach", object, state);
+    state.detach(object, runnables);
+  }
+
+  public InternalCDORevision read(InternalCDOObject object)
+  {
+    synchronized (getMonitor(object))
+    {
+      State state = getState(object, null, false);
+      traceEvent("Read", object, state);
+      return state.read(object);
+    }
+  }
+
+  public InternalCDORevision readNoLoad(InternalCDOObject object)
+  {
+    synchronized (getMonitor(object))
+    {
+      switch (object.cdoState())
+      {
+      case TRANSIENT:
+      case NEW:
+      case CONFLICT:
+      case INVALID_CONFLICT:
+      case INVALID:
+      case PROXY:
+        return null;
+      }
+
+      return object.cdoRevision();
+    }
+  }
+
+  public InternalCDORevision write(InternalCDOObject object, CDOFeatureDelta featureDelta)
+  {
+    synchronized (getMonitor(object))
+    {
+      return writeWithoutViewLock(object, featureDelta);
+    }
+  }
+
+  private InternalCDORevision writeWithoutViewLock(InternalCDOObject object, CDOFeatureDelta featureDelta)
+  {
+    State state = getState(object, null, false);
+    traceEvent("Write", object, state);
+    return state.write(object, featureDelta);
+  }
+
+  public void commit(InternalCDOObject object, CommitTransactionResult result)
+  {
+    synchronized (getMonitor(object))
+    {
+      State state = getState(object, null, false);
+      traceEvent("Commit", object, state);
+      state.commit(object, result);
+    }
+  }
+
+  public void rollback(InternalCDOObject object)
+  {
+    synchronized (getMonitor(object))
+    {
+      State state = getState(object, null, true);
+      traceEvent("Rollback", object, state);
+      state.rollback(object);
+    }
+  }
+
+  public void invalidate(InternalCDOObject object, CDORevisionKey key)
+  {
+    synchronized (getMonitor(object))
+    {
+      State state = getState(object, null, false);
+      traceEvent("Invalidate", object, state);
+      state.invalidate(object, key);
+    }
+  }
+
+  public void detachRemote(InternalCDOObject object)
+  {
+    synchronized (getMonitor(object))
+    {
+      State state = getState(object, null, false);
+      traceEvent("DetachRemote", object, state);
+      state.detachRemote(object);
+    }
+  }
+
+  private void transition(InternalCDOObject object, State state)
+  {
+    trace("   Transition " + getLabel(object) + " to " + state);
+    object.cdoInternalSetState(state.getCDOState());
+  }
+
+  private Object getMonitor(InternalCDOObject object)
+  {
+    InternalCDOView view = object.cdoView();
+    if (view != null)
+    {
+      return view;
+    }
+
+    // In TRANSIENT and PREPARED the object is not yet attached to a view
+    return object;
+  }
+
+  private State getState(InternalCDOObject object, InternalCDOTransaction transaction, boolean remove)
+  {
+    CDOState cdoState = object.cdoState();
+    switch (cdoState)
+    {
+    case TRANSIENT:
+      return getUnattachedState(object, transaction);
+
+    case NEW:
+    case DIRTY:
+    case PREPARED:
+      return getAttachedState(object, remove);
+
+    case CLEAN:
+      return CLEAN;
+
+    case PROXY:
+      return PROXY;
+
+    case CONFLICT:
+      return CONFLICT;
+
+    case INVALID:
+      return INVALID;
+
+    case INVALID_CONFLICT:
+      return INVALID_CONFLICT;
+
+    default:
+      throw new IllegalStateException("Illegal state: " + cdoState);
+    }
+  }
+
+  private State getUnattachedState(InternalCDOObject object, InternalCDOTransaction transaction)
+  {
+    if (transaction != null)
+    {
+      ChangeInfo detachedInfo = transaction.getLastSavepoint().getDetachedInfo(object);
+      if (detachedInfo != null)
+      {
+        return (State)detachedInfo;
+      }
+    }
+
+    return TRANSIENT;
+  }
+
+  private State getAttachedState(InternalCDOObject object, boolean remove)
+  {
+    InternalCDOSavepoint savepoint = object.cdoView().toTransaction().getLastSavepoint();
+    CDOID id = object.cdoID();
+
+    if (remove)
+    {
+      return (State)savepoint.removeChangeInfo(id);
+    }
+
+    return (State)savepoint.getChangeInfo(id);
+  }
+
+  private static String getLabel(InternalCDOObject object)
+  {
+    String label = object.toString();
+
+    int pos = label.indexOf('[');
+    if (pos != -1)
+    {
+      label = label.substring(0, pos);
+    }
+
+    return label;
+  }
+
+  private static void traceEvent(String event, InternalCDOObject object, State state)
+  {
+    int xxx;
+    // trace(event + " for " + getLabel(object) + " in " + state);
+  }
+
+  private static void trace(String message)
+  {
+    int xxx;
+    // System.out.println(message);
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  public static abstract class State
+  {
+    public static final String FAIL_PREFIX = "Impossible to handle ";
+
+    public abstract CDOState getCDOState();
+
+    public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
+    {
+      throw fail(object, "Attach");
+    }
+
+    public void detach(InternalCDOObject object, List<Runnable> runnables)
+    {
+      throw fail(object, "Detach");
+    }
+
+    public InternalCDORevision read(InternalCDOObject object)
+    {
+      throw fail(object, "Read");
+    }
+
+    public InternalCDORevision write(InternalCDOObject object, CDOFeatureDelta featureDelta)
+    {
+      throw fail(object, "Write");
+    }
+
+    public void commit(InternalCDOObject object, CommitTransactionResult result)
+    {
+      throw fail(object, "Commit");
+    }
+
+    public void rollback(InternalCDOObject object)
+    {
+      throw fail(object, "Rollback");
+    }
+
+    public void invalidate(InternalCDOObject object, CDORevisionKey key)
+    {
+      throw fail(object, "Invalidate");
+    }
+
+    public void detachRemote(InternalCDOObject object)
+    {
+      throw fail(object, "DetachRemote");
+    }
+
+    @Override
+    public String toString()
+    {
+      return getClass().getSimpleName().toUpperCase();
+    }
+
+    protected void doRemoteDetach(InternalCDOObject object)
+    {
+      INSTANCE.transition(object, INVALID);
+
+      InternalCDOView view = object.cdoView();
+      view.deregisterObject(object);
+      object.cdoInternalPostDetach(true);
+    }
+
+    protected final void ignore()
+    {
+      // trace("   IGNORE");
+    }
+
+    private IllegalStateException fail(InternalCDOObject object, String event)
+    {
+      String message = FAIL_PREFIX + event + " for " + getLabel(object) + " in " + this;
+      return new IllegalStateException(message);
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  public static abstract class Unattached extends State
+  {
+    @Override
+    public final CDOState getCDOState()
+    {
+      return CDOState.TRANSIENT;
+    }
+
+    @Override
+    public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
+    {
+      // Prepare content tree
+      for (Iterator<InternalEObject> it = getPersistentContents(object); it.hasNext();)
+      {
+        InternalEObject content = it.next();
+        Resource.Internal directResource = content.eDirectResource();
+
+        boolean objectIsResource = directResource == object;
+        if (objectIsResource || directResource == null)
+        {
+          InternalCDOObject adapted = FSMUtil.adapt(content, transaction);
+          INSTANCE.attach(adapted, transaction);
+        }
+      }
+    }
+
+    @Override
+    public void detach(InternalCDOObject object, List<Runnable> runnables)
+    {
+      ignore();
+    }
+
+    @Override
+    public final InternalCDORevision read(InternalCDOObject object)
+    {
+      // Can happen during detach
+      return object.cdoRevision();
+    }
+
+    @Override
+    public final InternalCDORevision write(InternalCDOObject object, CDOFeatureDelta featureDelta)
+    {
+      // Can happen during detach
+      return object.cdoRevision();
+    }
+
+    @Override
+    public void invalidate(InternalCDOObject object, CDORevisionKey key)
+    {
+      ignore();
+    }
+
+    @Override
+    public void detachRemote(InternalCDOObject object)
+    {
+      ignore();
+    }
+
+    protected void registerNewPackage(EClass eClass, InternalCDOSession session)
+    {
+      checkPackageRegistrationProblems(session, eClass);
+
+      EPackage ePackage = eClass.getEPackage();
+      CDOPackageRegistry packageRegistry = session.getPackageRegistry();
+      if (!packageRegistry.containsKey(ePackage.getNsURI()))
+      {
+        packageRegistry.putEPackage(ePackage);
+      }
+    }
+
+    private void checkPackageRegistrationProblems(InternalCDOSession session, EClass eClass)
+    {
+      if (session.options().isGeneratedPackageEmulationEnabled())
+      {
+        // Check that there are no multiple EPackages with the same URI in system. Bug 335004
+        String packageURI = eClass.getEPackage().getNsURI();
+        Object packageObject = session.getPackageRegistry().get(packageURI);
+        if (packageObject instanceof InternalCDOPackageInfo)
+        {
+          packageObject = ((InternalCDOPackageInfo)packageObject).getEPackage(false);
+        }
+
+        if (packageObject instanceof EPackage && packageObject != eClass.getEPackage())
+        {
+          throw new IllegalStateException(MessageFormat.format(
+              "Global EPackage {0} for EClass {1} is different from EPackage found in CDOPackageRegistry", packageURI,
+              eClass));
+        }
+      }
+    }
+
+    private Iterator<InternalEObject> getPersistentContents(InternalCDOObject object)
+    {
+      EStructuralFeature[] features = object.cdoClassInfo().getAllPersistentContainments();
+      return new EContentsEList.ResolvingFeatureIteratorImpl<InternalEObject>(object, features);
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Transient extends Unattached
+    {
+      @Override
+      public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
+      {
+        // TODO Permission check needed?
+        transaction.handleAttachingObject(object);
+
+        EClass eClass = object.eClass();
+        CDOID id = transaction.createIDForNewObject(object.cdoInternalInstance());
+        CDOBranchPoint branchPoint = transaction.getBranch().getHead();
+
+        InternalCDOSession session = transaction.getSession();
+        registerNewPackage(eClass, session);
+
+        // Create new revision
+        CDORevisionFactory revisionFactory = session.getRevisionManager().getFactory();
+        InternalCDORevision revision = (InternalCDORevision)revisionFactory.createRevision(eClass);
+        revision.setID(id);
+        revision.setBranchPoint(branchPoint);
+
+        object.cdoInternalSetView(transaction);
+        object.cdoInternalSetRevision(revision);
+
+        transaction.registerObject(object); // Object must have ID
+        object.cdoInternalPostAttach(); // Object must have CDOState.TRANSIENT and an empty revision
+
+        New newState = new New(object, null);
+        transaction.getLastSavepoint().addChangeInfo(newState);
+        INSTANCE.transition(object, newState);
+        super.attach(object, transaction);
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Detached extends Unattached implements ChangeInfo
+    {
+      private final InternalCDOObject object;
+
+      private final InternalCDORevision cleanRevision;
+
+      private final InternalCDORevision baseRevision;
+
+      public Detached(InternalCDOObject object, InternalCDORevision cleanRevision, InternalCDORevision baseRevision)
+      {
+        if (cleanRevision == null)
+        {
+          throw new IllegalArgumentException("cleanRevision is null");
+        }
+
+        this.object = object;
+        this.cleanRevision = cleanRevision;
+        this.baseRevision = baseRevision;
+      }
+
+      public ChangeType getType()
+      {
+        return ChangeType.DETACHED;
+      }
+
+      public CDOID getID()
+      {
+        return cleanRevision.getID();
+      }
+
+      public int getVersion()
+      {
+        return cleanRevision.getVersion();
+      }
+
+      public InternalCDOObject getObject()
+      {
+        return object;
+      }
+
+      public InternalCDORevision getCleanRevision()
+      {
+        return cleanRevision;
+      }
+
+      public InternalCDORevision getBaseRevision()
+      {
+        return baseRevision;
+      }
+
+      public InternalCDORevisionDelta getRevisionDelta()
+      {
+        return null;
+      }
+
+      public ChangeInfo setSavepoint()
+      {
+        InternalCDORevision newBaseRevision = null; // TODO Compute from instance
+        return new Detached(object, cleanRevision, newBaseRevision);
+      }
+
+      @Override
+      public void attach(InternalCDOObject object, InternalCDOTransaction transaction)
+      {
+        // TODO Permission check needed?
+        transaction.handleAttachingObject(object);
+
+        CDOID id = getID();
+        EClass eClass = object.eClass();
+        CDOBranchPoint branchPoint = transaction.getBranch().getHead();
+
+        InternalCDOSession session = transaction.getSession();
+        registerNewPackage(eClass, session);
+
+        InternalCDORevision cleanRevision = getCleanRevision();
+
+        // Create new revision
+        CDORevisionFactory revisionFactory = session.getRevisionManager().getFactory();
+        InternalCDORevision revision = (InternalCDORevision)revisionFactory.createRevision(eClass);
+        revision.setID(id);
+        revision.setVersion(cleanRevision.getVersion());
+        revision.setBranchPoint(branchPoint);
+
+        object.cdoInternalSetView(transaction);
+        object.cdoInternalSetRevision(revision);
+
+        transaction.registerObject(object); // Object must have ID
+        object.cdoInternalPostAttach(); // Object must have CDOState.TRANSIENT and an empty revision
+
+        InternalCDOSavepoint savepoint = transaction.getLastSavepoint();
+        savepoint.removeChangeInfo(id);
+
+        State newState;
+        InternalCDORevisionDelta revisionDelta = revision.compare(cleanRevision);
+        if (revisionDelta.isEmpty())
+        {
+          if (savepoint.getPreviousSavepoint() != null)
+          {
+            newState = new Undone(object, cleanRevision, baseRevision);
+            savepoint.addChangeInfo((ChangeInfo)newState);
+          }
+          else
+          {
+            newState = CLEAN;
+            savepoint.removeChangeInfo(id);
+          }
+        }
+        else
+        {
+          newState = new Dirty(object, cleanRevision, baseRevision, revisionDelta);
+          savepoint.addChangeInfo((ChangeInfo)newState);
+        }
+
+        INSTANCE.transition(object, newState);
+        transaction.updateDirtyState(true);
+        super.attach(object, transaction);
+      }
+
+      @Override
+      protected void registerNewPackage(EClass eClass, InternalCDOSession session)
+      {
+        // Do nothing
+      }
+
+      @Override
+      public String toString()
+      {
+        return super.toString() + "[" + cleanRevision + "]";
+      }
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  public static abstract class Attached extends State
+  {
+    public InternalCDORevision getCleanRevision(InternalCDOObject object)
+    {
+      // TODO What about states that are no ChangeInfo (i.e. not New or Dirty)?
+      return null;
+    }
+
+    public InternalCDORevision getBaseRevision()
+    {
+      // TODO What about states that are no ChangeInfo (i.e. not New or Dirty)?
+      return null;
+    }
+
+    @Override
+    public final void detach(final InternalCDOObject object, List<Runnable> runnables)
+    {
+      final InternalCDOTransaction transaction = object.cdoView().toTransaction();
+      transaction.handleDetachingObject(object);
+
+      runnables.add(new Runnable()
+      {
+        public void run()
+        {
+          State newState = doDetach(object, transaction.getLastSavepoint());
+
+          transaction.deregisterObject(object);
+          object.cdoInternalSetView(null);
+          object.cdoInternalSetID(null);
+
+          INSTANCE.transition(object, newState);
+        }
+      });
+
+      boolean isResource = object instanceof Resource;
+
+      // Prepare content tree
+      for (Iterator<EObject> it = object.eContents().iterator(); it.hasNext();)
+      {
+        InternalEObject eObject = (InternalEObject)it.next();
+        boolean isDirectlyConnected = isResource && eObject.eDirectResource() == object;
+        if (isDirectlyConnected || eObject.eDirectResource() == null)
+        {
+          InternalCDOObject adapted = FSMUtil.adapt(eObject, transaction);
+          if (adapted != null)
+          {
+            INSTANCE.detach(adapted, runnables);
+          }
+        }
+      }
+    }
+
+    protected State doDetach(InternalCDOObject object, InternalCDOSavepoint savepoint)
+    {
+      revisionToInstance(object);
+
+      InternalCDORevision cleanRevision = getCleanRevision(object);
+      InternalCDORevision baseRevision = getBaseRevision();
+
+      Detached detached = new Detached(object, cleanRevision, baseRevision);
+      savepoint.addChangeInfo(detached);
+      return detached;
+    }
+
+    private void revisionToInstance(InternalCDOObject object)
+    {
+      object.cdoInternalSetState(CDOState.TRANSIENT);
+    
+      try
+      {
+        object.cdoInternalPostDetach(false); // postDetach() requires the object to be TRANSIENT
+      }
+      finally
+      {
+        object.cdoInternalSetState(getCDOState());
+      }
+    }
+
+    @Override
+    public InternalCDORevision read(InternalCDOObject object)
+    {
+      // Ignore
+      return object.cdoRevision();
+    }
+
+    protected final InternalCDORevision getWritableRevision(InternalCDOObject object)
+    {
+      InternalCDORevision cleanRevision = object.cdoRevision();
+      if (!cleanRevision.isWritable())
+      {
+        throw new NoPermissionException(cleanRevision);
+      }
+
+      return cleanRevision;
+    }
+
+    protected final void doCommit(InternalCDOObject object, CommitTransactionResult result)
+    {
+      InternalCDOTransaction transaction = object.cdoView().toTransaction();
+      InternalCDORevision revision = object.cdoRevision();
+      Map<CDOID, CDOID> idMappings = result.getIDMappings();
+
+      // Adjust object
+      CDOID oldID = object.cdoID();
+      CDOID newID = idMappings.get(oldID);
+      if (newID != null)
+      {
+        revision.setID(newID);
+        transaction.remapObject(oldID);
+      }
+
+      // Adjust revision
+      revision.adjustForCommit(transaction.getBranch(), result.getTimeStamp());
+      revision.adjustReferences(result.getReferenceAdjuster());
+      // TODO Adjust possible CDOElementProxies!
+      revision.freeze();
+
+      InternalCDORevisionManager revisionManager = transaction.getSession().getRevisionManager();
+      revisionManager.addRevision(revision);
+
+      transaction.getLastSavepoint().removeChangeInfo(oldID);
+      INSTANCE.transition(object, CLEAN);
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Proxy extends Attached
+    {
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.PROXY;
+      }
+
+      @Override
+      public InternalCDORevision read(InternalCDOObject object)
+      {
+        load(object, false);
+        return object.cdoRevision();
+      }
+
+      @Override
+      public InternalCDORevision write(InternalCDOObject object, CDOFeatureDelta featureDelta)
+      {
+        load(object, true);
+        return INSTANCE.writeWithoutViewLock(object, featureDelta);
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        ignore();
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        doRemoteDetach(object);
+      }
+
+      protected final void load(InternalCDOObject object, boolean forWrite)
+      {
+        object.cdoInternalPreLoad();
+
+        InternalCDOView view = object.cdoView();
+        InternalCDORevision revision = view.getRevision(object.cdoID(), true);
+        if (revision == null)
+        {
+          INSTANCE.detachRemote(object);
+          CDOInvalidationPolicy policy = view.options().getInvalidationPolicy();
+          policy.handleInvalidObject(object);
+        }
+
+        if (forWrite && !revision.isWritable())
+        {
+          throw new NoPermissionException(revision);
+        }
+
+        object.cdoInternalSetRevision(revision);
+        INSTANCE.transition(object, CLEAN);
+        object.cdoInternalPostLoad();
+        view.dispatchLoadNotification(object);
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static class Clean extends Attached
+    {
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.CLEAN;
+      }
+
+      @Override
+      public InternalCDORevision getCleanRevision(InternalCDOObject object)
+      {
+        return object.cdoRevision();
+      }
+
+      @Override
+      public InternalCDORevision write(InternalCDOObject object, final CDOFeatureDelta featureDelta)
+      {
+        final InternalCDORevision cleanRevision = getWritableRevision(object);
+
+        InternalCDOTransaction transaction = object.cdoView().toTransaction();
+        transaction.handleModifyingObject(object, featureDelta);
+
+        InternalCDORevisionDelta revisionDelta = (InternalCDORevisionDelta)CDORevisionUtil.createDelta(cleanRevision);
+        boolean mergeIsEmpty = revisionDelta.mergeFeatureDelta(featureDelta, new CDOOriginSizeProvider.Caching()
+        {
+          @Override
+          protected CDOList getList()
+          {
+            EStructuralFeature feature = featureDelta.getFeature();
+            return cleanRevision.getList(feature);
+          }
+        });
+
+        if (mergeIsEmpty)
+        {
+          return cleanRevision;
+        }
+
+        // Copy revision
+        InternalCDORevision revision = cleanRevision.copy();
+        featureDelta.apply(revision);
+        object.cdoInternalSetRevision(revision);
+
+        Dirty dirty = new Dirty(object, cleanRevision, cleanRevision, revisionDelta);
+        transaction.getLastSavepoint().addChangeInfo(dirty);
+
+        INSTANCE.transition(object, dirty);
+        transaction.updateDirtyState(false);
+
+        return revision;
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        InternalCDORevision oldRevision = object.cdoRevision();
+        InternalCDORevision newRevision = null;
+
+        InternalCDOView view = object.cdoView();
+        InternalCDORevisionCache cache = view.getSession().getRevisionManager().getCache();
+
+        if (SWITCHING_TARGET.get() == Boolean.TRUE)
+        {
+          CDORevisionDelta delta = (CDORevisionDelta)key;
+          CDORevisable target = delta.getTarget();
+          newRevision = (InternalCDORevision)cache.getRevisionByVersion(delta.getID(), target);
+          if (newRevision == null)
+          {
+            newRevision = oldRevision.copy();
+            view.getSession().resolveAllElementProxies(newRevision);
+            delta.apply(newRevision);
+            newRevision.setBranchPoint(target);
+            cache.addRevision(newRevision);
+          }
+
+          object.cdoInternalSetRevision(newRevision);
+          INSTANCE.transition(object, CLEAN);
+          object.cdoInternalPostLoad();
+          return;
+        }
+
+        if (key == null || key.getVersion() >= oldRevision.getVersion())
+        {
+          CDORevisionKey newKey = null;
+          if (key != null)
+          {
+            int newVersion = getNewVersion(key);
+            newKey = CDORevisionUtil.createRevisionKey(key.getID(), key.getBranch(), newVersion);
+          }
+
+          if (newKey != null)
+          {
+            newRevision = (InternalCDORevision)cache.getRevisionByVersion(newKey.getID(), newKey);
+          }
+
+          if (newRevision != null)
+          {
+            object.cdoInternalSetRevision(newRevision);
+            INSTANCE.transition(object, CLEAN);
+            object.cdoInternalPostLoad();
+          }
+          else
+          {
+            INSTANCE.transition(object, PROXY);
+
+            CDOInvalidationPolicy policy = view.options().getInvalidationPolicy();
+            policy.handleInvalidation(object, key);
+            object.cdoInternalPostInvalidate();
+          }
+        }
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        doRemoteDetach(object);
+      }
+
+      private static int getNewVersion(CDORevisionKey key)
+      {
+        if (key instanceof CDORevisionDelta)
+        {
+          CDORevisionDelta delta = (CDORevisionDelta)key;
+          CDORevisable target = delta.getTarget();
+          if (target != null && key.getBranch() == target.getBranch())
+          {
+            return target.getVersion();
+          }
+        }
+
+        return key.getVersion() + 1;
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Undone extends Clean implements ChangeInfo
+    {
+      private final InternalCDOObject object;
+
+      private final InternalCDORevision cleanRevision;
+
+      private final InternalCDORevision baseRevision;
+
+      public Undone(InternalCDOObject object, InternalCDORevision cleanRevision, InternalCDORevision baseRevision)
+      {
+        this.object = object;
+        this.cleanRevision = cleanRevision;
+        this.baseRevision = baseRevision;
+      }
+
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.PREPARED;
+      }
+
+      public ChangeType getType()
+      {
+        return ChangeType.UNDONE;
+      }
+
+      public CDOID getID()
+      {
+        return object.cdoID();
+      }
+
+      public InternalCDOObject getObject()
+      {
+        return object;
+      }
+
+      @Override
+      public InternalCDORevision getCleanRevision(InternalCDOObject object)
+      {
+        return cleanRevision;
+      }
+
+      public InternalCDORevision getCleanRevision()
+      {
+        return cleanRevision;
+      }
+
+      @Override
+      public InternalCDORevision getBaseRevision()
+      {
+        return baseRevision;
+      }
+
+      public InternalCDORevisionDelta getRevisionDelta()
+      {
+        return null;
+      }
+
+      public ChangeInfo setSavepoint()
+      {
+        InternalCDORevision newBaseRevision = object.cdoRevision().copy();
+        return new Undone(object, cleanRevision, newBaseRevision);
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class New extends Attached implements ChangeInfo
+    {
+      private final InternalCDOObject object;
+
+      private final InternalCDORevision baseRevision;
+
+      public New(InternalCDOObject object, InternalCDORevision baseRevision)
+      {
+        this.object = object;
+        this.baseRevision = baseRevision;
+      }
+
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.NEW;
+      }
+
+      public ChangeType getType()
+      {
+        return ChangeType.NEW;
+      }
+
+      public CDOID getID()
+      {
+        return object.cdoID();
+      }
+
+      public int getVersion()
+      {
+        return CDOBranchVersion.UNSPECIFIED_VERSION;
+      }
+
+      public InternalCDOObject getObject()
+      {
+        return object;
+      }
+
+      @Override
+      public InternalCDORevision getCleanRevision(InternalCDOObject object)
+      {
+        return null;
+      }
+
+      public InternalCDORevision getCleanRevision()
+      {
+        return null;
+      }
+
+      @Override
+      public InternalCDORevision getBaseRevision()
+      {
+        return baseRevision;
+      }
+
+      public InternalCDORevisionDelta getRevisionDelta()
+      {
+        return null;
+      }
+
+      public ChangeInfo setSavepoint()
+      {
+        InternalCDORevision newBaseRevision = object.cdoRevision().copy();
+        return new New(object, newBaseRevision);
+      }
+
+      @Override
+      public InternalCDORevision write(InternalCDOObject object, CDOFeatureDelta featureDelta)
+      {
+        InternalCDORevision revision = getWritableRevision(object); // Check write permission
+
+        InternalCDOTransaction transaction = object.cdoView().toTransaction();
+        transaction.handleModifyingObject(object, featureDelta);
+
+        featureDelta.apply(revision);
+        return revision;
+      }
+
+      @Override
+      public void commit(InternalCDOObject object, CommitTransactionResult result)
+      {
+        doCommit(object, result);
+      }
+
+      @Override
+      public String toString()
+      {
+        return super.toString() + "[" + object.cdoRevision() + "]";
+      }
+
+      @Override
+      protected State doDetach(InternalCDOObject object, InternalCDOSavepoint savepoint)
+      {
+        savepoint.removeChangeInfo(object.cdoID());
+        return TRANSIENT;
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Dirty extends Attached implements ChangeInfo
+    {
+      private final InternalCDOObject object;
+
+      private final InternalCDORevision cleanRevision;
+
+      private final InternalCDORevision baseRevision;
+
+      private InternalCDORevisionDelta revisionDelta;
+
+      public Dirty(InternalCDOObject object, InternalCDORevision cleanRevision, InternalCDORevision baseRevision,
+          InternalCDORevisionDelta revisionDelta)
+      {
+        this.object = object;
+        this.cleanRevision = cleanRevision;
+        this.baseRevision = baseRevision;
+        this.revisionDelta = revisionDelta;
+      }
+
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.DIRTY;
+      }
+
+      public ChangeType getType()
+      {
+        return ChangeType.DIRTY;
+      }
+
+      public CDOID getID()
+      {
+        return cleanRevision.getID();
+      }
+
+      public int getVersion()
+      {
+        return cleanRevision.getVersion();
+      }
+
+      public InternalCDOObject getObject()
+      {
+        return object;
+      }
+
+      @Override
+      public InternalCDORevision getCleanRevision(InternalCDOObject object)
+      {
+        return cleanRevision;
+      }
+
+      public InternalCDORevision getCleanRevision()
+      {
+        return cleanRevision;
+      }
+
+      @Override
+      public InternalCDORevision getBaseRevision()
+      {
+        return baseRevision;
+      }
+
+      public InternalCDORevisionDelta getRevisionDelta()
+      {
+        return revisionDelta;
+      }
+
+      public ChangeInfo setSavepoint()
+      {
+        InternalCDORevision newBaseRevision = object.cdoRevision().copy();
+        return new Dirty(object, cleanRevision, newBaseRevision, revisionDelta);
+      }
+
+      @Override
+      public InternalCDORevision write(InternalCDOObject object, final CDOFeatureDelta featureDelta)
+      {
+        InternalCDORevision revision = getWritableRevision(object);
+
+        InternalCDOTransaction transaction = object.cdoView().toTransaction();
+        transaction.handleModifyingObject(object, featureDelta);
+
+        final EStructuralFeature feature = featureDelta.getFeature();
+        featureDelta.apply(revision);
+
+        if (revision.isUnchunked())
+        {
+          if (isOnlyFeatureDelta(feature))
+          {
+            if (isClean(revision, feature))
+            {
+              undo(object, transaction);
+              return cleanRevision;
+            }
+          }
+        }
+
+        boolean mergeIsEmpty = revisionDelta.mergeFeatureDelta(featureDelta, new CDOOriginSizeProvider.Caching()
+        {
+          @Override
+          protected CDOList getList()
+          {
+            return cleanRevision.getList(feature);
+          }
+        });
+
+        if (mergeIsEmpty && revisionDelta.isEmpty())
+        {
+          undo(object, transaction);
+          return cleanRevision;
+        }
+
+        return revision;
+      }
+
+      private boolean isOnlyFeatureDelta(final EStructuralFeature feature)
+      {
+        Map<EStructuralFeature, CDOFeatureDelta> featureDeltas = revisionDelta.getFeatureDeltaMap();
+        return featureDeltas.size() == 1 && featureDeltas.containsKey(feature);
+      }
+
+      private boolean isClean(InternalCDORevision revision, EStructuralFeature feature)
+      {
+        if (feature.isMany())
+        {
+          CDOList list = revision.getList(feature);
+          CDOList cleanList = cleanRevision.getList(feature);
+
+          int size = list.size();
+          if (size != cleanList.size())
+          {
+            return false;
+          }
+
+          for (int i = size - 1; i >= 0; --i)
+          {
+            Object value = list.get(i);
+            Object cleanValue = cleanList.get(i);
+            if (!CDORevisionUtil.areValuesEqual(value, cleanValue))
+            {
+              return false;
+            }
+          }
+
+          return true;
+        }
+
+        Object value = revision.getValue(feature);
+        Object cleanValue = cleanRevision.getValue(feature);
+        return CDORevisionUtil.areValuesEqual(value, cleanValue);
+      }
+
+      private void undo(InternalCDOObject object, InternalCDOTransaction transaction)
+      {
+        object.cdoInternalSetRevision(cleanRevision);
+
+        State newState;
+        InternalCDOSavepoint savepoint = transaction.getLastSavepoint();
+        if (savepoint.getPreviousSavepoint() != null)
+        {
+          newState = new Undone(object, cleanRevision, baseRevision);
+          savepoint.addChangeInfo((ChangeInfo)newState);
+        }
+        else
+        {
+          newState = CLEAN;
+          savepoint.removeChangeInfo(getID());
+        }
+
+        INSTANCE.transition(object, newState);
+        transaction.updateDirtyState(true);
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        InternalCDORevision oldRevision = object.cdoRevision();
+        if (key == null || key.getVersion() >= oldRevision.getVersion() - 1)
+        {
+          INSTANCE.transition(object, CONFLICT);
+          InternalCDOTransaction transaction = object.cdoView().toTransaction();
+          transaction.setConflict(object);
+        }
+      }
+
+      @Override
+      public void commit(InternalCDOObject object, CommitTransactionResult result)
+      {
+        doCommit(object, result);
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        INSTANCE.transition(object, INVALID_CONFLICT);
+
+        InternalCDOTransaction transaction = object.cdoView().toTransaction();
+        transaction.setConflict(object);
+      }
+
+      @Override
+      public String toString()
+      {
+        return super.toString() + "[" + getRevisionDelta() + "]";
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Conflict extends Attached
+    {
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.CONFLICT;
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        ignore();
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        ignore();
+      }
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  public static abstract class Unusable extends State
+  {
+    @Override
+    public void detach(InternalCDOObject object, List<Runnable> runnables)
+    {
+      ignore();
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Invalid extends Unusable
+    {
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.INVALID;
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        ignore();
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        ignore();
+      }
+    }
+
+    /**
+     * @author Eike Stepper
+     */
+    public static final class Invalid_Conflict extends Unusable
+    {
+      @Override
+      public CDOState getCDOState()
+      {
+        return CDOState.INVALID_CONFLICT;
+      }
+
+      @Override
+      public void invalidate(InternalCDOObject object, CDORevisionKey key)
+      {
+        ignore();
+      }
+
+      @Override
+      public void rollback(InternalCDOObject object)
+      {
+        doRemoteDetach(object);
+      }
+
+      @Override
+      public void detachRemote(InternalCDOObject object)
+      {
+        ignore();
+      }
+    }
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStoreImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStoreImpl.java
index 9b9cabd..58d64c9 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStoreImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStoreImpl.java
@@ -54,6 +54,7 @@
 import org.eclipse.emf.spi.cdo.CDOStore;
 import org.eclipse.emf.spi.cdo.FSMUtil;
 import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
 import org.eclipse.emf.spi.cdo.InternalCDOView;
 
 import java.text.MessageFormat;
@@ -110,10 +111,7 @@
       CDOID newResourceID = newResource == null ? CDOID.NULL : newResource.cdoID();
 
       CDOFeatureDelta delta = new CDOContainerFeatureDeltaImpl(newResourceID, newContainerID, newContainerFeatureID);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-      revision.setResourceID(newResourceID);
-      revision.setContainerID(newContainerID);
-      revision.setContainingFeatureID(newContainerFeatureID);
+      getRevisionForWriting(cdoObject, delta);
     }
   }
 
@@ -397,15 +395,18 @@
         TRACER.format("set({0}, {1}, {2}, {3})", cdoObject, feature, index, value); //$NON-NLS-1$
       }
 
-      value = convertToCDO(cdoObject, feature, value);
-
       InternalCDORevision oldRevision = getRevisionForReading(cdoObject);
       Object oldValue = oldRevision.get(feature, index);
       oldValue = convertToEMF(eObject, oldRevision, feature, index, oldValue);
 
-      CDOFeatureDelta delta = new CDOSetFeatureDeltaImpl(feature, index, value, oldValue);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-      revision.set(feature, index, value);
+      if (!ObjectUtil.equals(value, oldValue))
+      {
+        value = convertToCDO(cdoObject, feature, value);
+
+        CDOFeatureDelta delta = new CDOSetFeatureDeltaImpl(feature, index, value, oldValue);
+        InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
+        revision.set(feature, index, value);
+      }
 
       return oldValue;
     }
@@ -422,28 +423,7 @@
       }
 
       CDOFeatureDelta delta = new CDOUnsetFeatureDeltaImpl(feature);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-
-      if (feature.isUnsettable())
-      {
-        revision.unset(feature);
-      }
-      else
-      {
-        if (feature.isMany())
-        {
-          Object value = revision.getValue(feature);
-
-          @SuppressWarnings("unchecked")
-          List<Object> list = (List<Object>)value;
-          list.clear();
-        }
-        else
-        {
-          Object defaultValue = convertToCDO(cdoObject, feature, feature.getDefaultValue());
-          revision.set(feature, NO_INDEX, defaultValue);
-        }
-      }
+      getRevisionForWriting(cdoObject, delta);
     }
   }
 
@@ -457,18 +437,13 @@
         TRACER.format("add({0}, {1}, {2}, {3})", cdoObject, feature, index, value); //$NON-NLS-1$
       }
 
-      if (feature.isMany())
-      {
-        value = convertToCDO(cdoObject, feature, value);
-      }
-      else
-      {
-        throw new UnsupportedOperationException("ADD is not supported for single-valued features");
-      }
+      checkManyValued(feature);
+      value = convertToCDO(cdoObject, feature, value);
 
       CDOFeatureDelta delta = new CDOAddFeatureDeltaImpl(feature, index, value);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-      revision.add(feature, index, value);
+      getRevisionForWriting(cdoObject, delta);
+      // InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
+      // revision.add(feature, index, value);
     }
   }
 
@@ -482,42 +457,50 @@
         TRACER.format("remove({0}, {1}, {2})", cdoObject, feature, index); //$NON-NLS-1$
       }
 
-      Object oldValue = null;
+      Object oldValue = getListElement(cdoObject, feature, index);
+      oldValue = convertToEMF(eObject, cdoObject.cdoRevision(), feature, index, oldValue);
 
-      // Bug 293283 / Bug 314387
-      if (feature.isMany())
-      {
-        InternalCDORevision readLockedRevision = getRevisionForReading(cdoObject);
-        CDOList list = readLockedRevision.getList(feature);
-        int size = list.size();
-        if (index < 0 || size <= index)
-        {
-          throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
-        }
-      }
-      else
-      {
-        throw new UnsupportedOperationException("REMOVE is not supported for single-valued features");
-      }
+      CDOFeatureDelta delta = new CDORemoveFeatureDeltaImpl(feature, index, oldValue);
+      getRevisionForWriting(cdoObject, delta);
 
-      CDOFeatureDelta delta = new CDORemoveFeatureDeltaImpl(feature, index);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-
-      oldValue = revision.get(feature, index);
-
-      try
-      {
-        oldValue = convertToEMF(eObject, revision, feature, index, oldValue);
-      }
-      finally
-      {
-        revision.remove(feature, index);
-      }
+      // InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
+      // try
+      // {
+      // oldValue = convertToEMF(eObject, revision, feature, index, oldValue);
+      // }
+      // finally
+      // {
+      // revision.remove(feature, index);
+      // }
 
       return oldValue;
     }
   }
 
+  private Object getListElement(InternalCDOObject object, EStructuralFeature feature, int index)
+  {
+    checkManyValued(feature);
+
+    // Bug 293283 / Bug 314387
+    InternalCDORevision readLockedRevision = getRevisionForReading(object);
+    CDOList list = readLockedRevision.getList(feature);
+    int size = list.size();
+    if (index < 0 || size <= index)
+    {
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+    }
+
+    return readLockedRevision.get(feature, index);
+  }
+
+  private void checkManyValued(EStructuralFeature feature)
+  {
+    if (!feature.isMany())
+    {
+      throw new UnsupportedOperationException("Single-valued features have no list elements");
+    }
+  }
+
   public void clear(InternalEObject eObject, EStructuralFeature feature)
   {
     synchronized (view)
@@ -529,9 +512,10 @@
       }
 
       CDOFeatureDelta delta = new CDOClearFeatureDeltaImpl(feature);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-      // TODO Handle containment remove!!!
-      revision.clear(feature);
+      getRevisionForWriting(cdoObject, delta);
+      // InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
+      // // TODO Handle containment remove!!!
+      // revision.clear(feature);
     }
   }
 
@@ -545,12 +529,14 @@
         TRACER.format("move({0}, {1}, {2}, {3})", cdoObject, feature, target, source); //$NON-NLS-1$
       }
 
-      CDOFeatureDelta delta = new CDOMoveFeatureDeltaImpl(feature, target, source);
-      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
-      Object result = revision.move(feature, target, source);
+      Object value = getListElement(cdoObject, feature, source);
+      CDOFeatureDelta delta = new CDOMoveFeatureDeltaImpl(feature, target, source, value);
 
-      result = convertToEMF(eObject, revision, feature, target, result);
-      return result;
+      InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
+      // value = revision.move(feature, target, source);
+
+      value = convertToEMF(eObject, revision, feature, target, value);
+      return value;
     }
   }
 
@@ -644,11 +630,13 @@
             CDOID id = (CDOID)value;
             CDOList list = revision.getList(feature);
             CDORevisionPrefetchingPolicy policy = view.options().getRevisionPrefetchingPolicy();
-            InternalCDORevisionManager revisionManager = view.getSession().getRevisionManager();
+
+            InternalCDOSession session = view.getSession();
+            InternalCDORevisionManager revisionManager = session.getRevisionManager();
             List<CDOID> listOfIDs = policy.loadAhead(revisionManager, view, eObject, feature, list, index, id);
             if (!listOfIDs.isEmpty())
             {
-              int initialChunkSize = view.getSession().options().getCollectionLoadingPolicy().getInitialChunkSize();
+              int initialChunkSize = session.options().getCollectionLoadingPolicy().getInitialChunkSize();
               revisionManager.getRevisions(listOfIDs, view, initialChunkSize, CDORevision.DEPTH_NONE, true);
             }
           }
@@ -718,19 +706,21 @@
 
   private static InternalCDORevision getRevisionForReading(InternalCDOObject cdoObject)
   {
-    CDOStateMachine.INSTANCE.read(cdoObject);
-    return getRevision(cdoObject);
+    return safe(CDOStateMachine2.INSTANCE.read(cdoObject));
   }
 
   private static InternalCDORevision getRevisionForWriting(InternalCDOObject cdoObject, CDOFeatureDelta delta)
   {
-    CDOStateMachine.INSTANCE.write(cdoObject, delta);
-    return getRevision(cdoObject);
+    return safe(CDOStateMachine2.INSTANCE.write(cdoObject, delta));
   }
 
   private static InternalCDORevision getRevision(InternalCDOObject cdoObject)
   {
-    InternalCDORevision revision = cdoObject.cdoRevision();
+    return safe(cdoObject.cdoRevision());
+  }
+
+  private static InternalCDORevision safe(InternalCDORevision revision)
+  {
     if (revision == null)
     {
       throw new IllegalStateException("revision == null");
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
index 7d0c16a..4a0916c 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
@@ -242,13 +242,13 @@
 
     try
     {
-      CDOStateMachine.SWITCHING_TARGET.set(Boolean.TRUE);
+      CDOStateMachine2.SWITCHING_TARGET.set(Boolean.TRUE);
       doInvalidate(branchPoint.getBranch(), CDOBranchPoint.UNSPECIFIED_DATE, allChangedObjects, allDetachedObjects,
           oldRevisions, true);
     }
     finally
     {
-      CDOStateMachine.SWITCHING_TARGET.remove();
+      CDOStateMachine2.SWITCHING_TARGET.remove();
     }
 
     IListener[] listeners = getListeners();
@@ -504,7 +504,7 @@
     InternalCDORevision revision = (InternalCDORevision)object.cdoRevision();
     if (revision == null)
     {
-      revision = CDOStateMachine.INSTANCE.read((InternalCDOObject)object);
+      revision = CDOStateMachine2.INSTANCE.read((InternalCDOObject)object);
     }
 
     return revision;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/AbstractObjectConflictResolver.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/AbstractObjectConflictResolver.java
index e806f97..08e64b7 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/AbstractObjectConflictResolver.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/AbstractObjectConflictResolver.java
@@ -36,7 +36,7 @@
 import org.eclipse.emf.internal.cdo.bundle.OM;
 import org.eclipse.emf.internal.cdo.messages.Messages;
 import org.eclipse.emf.internal.cdo.object.CDOObjectMerger;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
 
 import org.eclipse.net4j.util.collection.Pair;
 import org.eclipse.net4j.util.event.IEvent;
@@ -129,12 +129,12 @@
   @Deprecated
   public static void rollbackObject(CDOObject object)
   {
-    CDOStateMachine.INSTANCE.rollback((InternalCDOObject)object);
+    CDOStateMachine2.INSTANCE.rollback((InternalCDOObject)object);
   }
 
   public static void readObject(CDOObject object)
   {
-    CDOStateMachine.INSTANCE.read((InternalCDOObject)object);
+    CDOStateMachine2.INSTANCE.read((InternalCDOObject)object);
   }
 
   /**
@@ -377,7 +377,7 @@
         for (CDOFeatureDelta remoteFeatureDelta : remoteDelta.getFeatureDeltas())
         {
           // TODO Add public API for this:
-          ((InternalCDORevisionDelta)localDelta).addFeatureDelta(remoteFeatureDelta, null);
+          ((InternalCDORevisionDelta)localDelta).mergeFeatureDelta(remoteFeatureDelta, null);
         }
       }
 
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 debb6d3..d7ff1ef 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
@@ -105,7 +105,6 @@
     InternalCDOTransaction transaction = (InternalCDOTransaction)getTransaction();
     InternalCDOSavepoint savepoint = transaction.getLastSavepoint();
 
-    Map<InternalCDOObject, InternalCDORevision> cleanRevisions = transaction.getCleanRevisions();
     Map<CDOID, CDOObject> dirtyObjects = savepoint.getDirtyObjects();
     // final ObjectsMapUpdater newObjectsUpdater = new ObjectsMapUpdater(savepoint.getNewObjects());
     final ObjectsMapUpdater detachedObjectsUpdater = new ObjectsMapUpdater(savepoint.getDetachedObjects());
@@ -125,7 +124,7 @@
         int newVersion = localRevision.getVersion() + 1;
 
         // Compute new local revision
-        InternalCDORevision cleanRevision = cleanRevisions.get(object);
+        InternalCDORevision cleanRevision = transaction.getCleanRevision(object);
         if (cleanRevision == null)
         {
           // In this case the object revision *is clean*
@@ -159,7 +158,8 @@
           localDeltas.put(id, newLocalDelta);
           object.cdoInternalSetState(CDOState.DIRTY);
 
-          cleanRevisions.put(object, newCleanRevision);
+          int xxx;
+          // cleanRevisions.put(object, newCleanRevision);
           dirtyObjects.put(id, object);
 
           newLocalDelta.accept(new CDOFeatureDeltaVisitorImpl()
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 7129872..03219df 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
@@ -548,7 +548,7 @@
           CDOFeatureDelta featureDelta = changedInTarget(targetFeatureDelta);
           if (featureDelta != null)
           {
-            result.addFeatureDelta(featureDelta, null);
+            result.mergeFeatureDelta(featureDelta, null);
           }
         }
         else
@@ -556,7 +556,7 @@
           CDOFeatureDelta featureDelta = changedInSourceAndTarget(targetFeatureDelta, sourceFeatureDelta);
           if (featureDelta != null)
           {
-            result.addFeatureDelta(featureDelta, null);
+            result.mergeFeatureDelta(featureDelta, null);
           }
           else
           {
@@ -583,8 +583,8 @@
               }
             }
 
-            ((InternalCDORevisionDelta)conflict.getTargetDelta()).addFeatureDelta(targetFeatureDelta, null);
-            ((InternalCDORevisionDelta)conflict.getSourceDelta()).addFeatureDelta(sourceFeatureDelta, null);
+            ((InternalCDORevisionDelta)conflict.getTargetDelta()).mergeFeatureDelta(targetFeatureDelta, null);
+            ((InternalCDORevisionDelta)conflict.getSourceDelta()).mergeFeatureDelta(sourceFeatureDelta, null);
           }
         }
       }
@@ -599,7 +599,7 @@
           CDOFeatureDelta featureDelta = changedInSource(sourceFeatureDelta);
           if (featureDelta != null)
           {
-            result.addFeatureDelta(featureDelta, null);
+            result.mergeFeatureDelta(featureDelta, null);
           }
         }
       }
@@ -781,7 +781,7 @@
 
         for (int i = 0; i < originSize; i++)
         {
-          expandedDeltas.add(new CDORemoveFeatureDeltaImpl(feature, 0));
+          expandedDeltas.add(new CDORemoveFeatureDeltaImpl(feature, 0, CDOFeatureDelta.UNKNOWN_VALUE));
         }
 
         return expandedDeltas;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/FSMUtil.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/FSMUtil.java
index 3628e4a..4e875db 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/FSMUtil.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/FSMUtil.java
@@ -43,7 +43,7 @@
   public static boolean isTransient(CDOObject object)
   {
     CDOState state = object.cdoState();
-    return state == CDOState.TRANSIENT || state == CDOState.PREPARED;
+    return state == CDOState.TRANSIENT;
   }
 
   public static boolean isInvalid(CDOObject object)
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java
index d025792..d6ef1b5 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java
@@ -42,9 +42,19 @@
 
   public InternalCDORevision cdoRevision();
 
+  /**
+   * Migrates the object values from the instance to the revision.
+   * <p>
+   * This object must be in state PREPARED, so that eStore() uses the instance and cdoStore() uses the revision. 
+   */
   public void cdoInternalPostAttach();
 
-  public void cdoInternalPostDetach(boolean remote);
+  /**
+   * Migrates the object values from the revision to the instance.
+   * <p>
+   * This object must be in state TRANSIENT, so that eStore() uses the instance and cdoStore() uses the revision. 
+   */
+  public void cdoInternalPostDetach(boolean remote); // TODO Rename to preDetach
 
   public void cdoInternalPostInvalidate();
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java
index 3e1469c..91d6b30 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSavepoint.java
@@ -10,14 +10,28 @@
  */
 package org.eclipse.emf.spi.cdo;
 
+import org.eclipse.emf.cdo.CDOObject;
 import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIdentifiable;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
 import org.eclipse.emf.cdo.transaction.CDOSavepoint;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
 
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
+
+import java.util.Map;
 import java.util.Set;
 
 /**
- * If the meaning of this type isn't clear, there really should be more of a description here...
- *
+ * A {@link CDOSavepoint} has to capture enough data to support the following use cases:
+ * <ul> 
+ * <li> <b>Detection</b> of undo operations (netted out deltas) in comparison to the transaction's baseline. 
+ * <li> <b>Commit</b> of the entire {@link CDOTransaction}. 
+ * <li> <b>Rollback</b> to the initial state of this savepoint.
+ * <li> <b>Support</b> of all other {@link CDOStateMachine2} transitions.
+ * </ul>
+ *   
  * @author Eike Stepper
  * @since 3.0
  * @noextend This interface is not intended to be extended by clients.
@@ -33,8 +47,33 @@
 
   public InternalCDOSavepoint getNextSavepoint();
 
+  public ChangeInfo addChangeInfo(ChangeInfo changeInfo);
+
+  public ChangeInfo removeChangeInfo(CDOObject object);
+
+  public ChangeInfo removeChangeInfo(CDOID id);
+
+  public ChangeInfo getChangeInfo(CDOID id);
+
+  public ChangeInfo getDetachedInfo(CDOObject object);
+
+  public Map<CDOID, ChangeInfo> getChangeInfos();
+
+  public Map<InternalCDOObject, ChangeInfo> getDetachedInfos();
+
+  /**
+   * @since 4.1
+   */
+  public boolean isNewObject(CDOID id);
+
+  public boolean isDetachedObject(CDOID id);
+
   public void clear();
 
+  public boolean isInMemory();
+
+  public void setInMemory(boolean inMemory);
+
   @Deprecated
   public Set<CDOID> getSharedDetachedObjects();
 
@@ -42,7 +81,58 @@
   public void recalculateSharedDetachedObjects();
 
   /**
-   * @since 4.1
+   * Adds the given object to this savepoint and returns <code>true</code> if it was TRANSIENT, 
+   * <code>false</code> otherwise (DETACHED).
+   * 
+   * @deprecated As of 4.3 no longer supported.
    */
-  public boolean isNewObject(CDOID id);
+  @Deprecated
+  public void attachObject(InternalCDOObject object);
+
+  /**
+   * Removes the given object from this savepoint and returns <code>true</code> if it was NEW, 
+   * <code>false</code> otherwise (CLEAN | DIRTY).
+   * 
+   * @deprecated As of 4.3 no longer supported.
+   */
+  @Deprecated
+  public void removeObject(InternalCDOObject object);
+
+  /**
+   * @author Eike Stepper
+   */
+  public interface ChangeInfo extends CDOIdentifiable
+  {
+    public ChangeType getType();
+
+    /**
+     * Returns the object or <code>null</code>.
+     */
+    public InternalCDOObject getObject();
+
+    /**
+     * Returns the revision delta or <code>null</code>.
+     */
+    public InternalCDORevisionDelta getRevisionDelta();
+
+    /**
+     * Returns the clean revision or <code>null</code>. The clean revision is used to identify undoing operations.
+     */
+    public InternalCDORevision getCleanRevision();
+
+    /**
+     * Returns the base revision or <code>null</code>. The base revision is used to rollback to savepoints.
+     */
+    public InternalCDORevision getBaseRevision();
+
+    public ChangeInfo setSavepoint();
+
+    /**
+     * @author Eike Stepper
+     */
+    public enum ChangeType
+    {
+      NEW, DIRTY, CONFLICT, UNDONE, DETACHED
+    }
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java
index d8aceb5..5021434 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java
@@ -11,6 +11,7 @@
  */
 package org.eclipse.emf.spi.cdo;
 
+import org.eclipse.emf.cdo.CDOObject;
 import org.eclipse.emf.cdo.common.branch.CDOBranch;
 import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
 import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
@@ -27,6 +28,7 @@
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 
 import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.event.IListener;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
@@ -77,38 +79,37 @@
   public void setTransactionStrategy(CDOTransactionStrategy transactionStrategy);
 
   /**
-   * @return never <code>null</code>;
+   * @return Never <code>null</code>;
    */
   public CDOResourceFolder getOrCreateResourceFolder(List<String> names);
 
-  public void detachObject(InternalCDOObject object);
+  public void attachObject(InternalCDOObject object);
 
-  /**
-   * @deprecated {@link #createIDForNewObject(EObject)} is called since 4.1.
-   */
-  @Deprecated
-  public CDOIDTemp getNextTemporaryID();
+  public void detachObject(InternalCDOObject object);
 
   /**
    * @since 4.1
    */
   public CDOID createIDForNewObject(EObject object);
 
+  public void handleAttachingObject(CDOObject object);
+
+  public void handleModifyingObject(CDOObject object, CDOFeatureDelta featureDelta);
+
+  public void handleDetachingObject(CDOObject object);
+
   /**
    * @since 4.0
    */
-  public void registerAttached(InternalCDOObject object, boolean isNew);
+  public void _registerAttached(InternalCDOObject object, boolean isNew);
 
-  public void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta);
+  public void _registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta);
 
-  public void registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta);
+  public void _registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta);
 
-  public void registerRevisionDelta(CDORevisionDelta revisionDelta);
+  public void _registerRevisionDelta(CDORevisionDelta revisionDelta);
 
-  /**
-   * @since 4.2
-   */
-  public void setDirty(boolean dirty);
+  public IListener[] updateDirtyState(boolean undone);
 
   public void setConflict(InternalCDOObject object);
 
@@ -117,20 +118,6 @@
    *          May be <code>null</code> if changeSetData does not result from a
    *          {@link #merge(CDOBranchPoint, org.eclipse.emf.cdo.transaction.CDOMerger) merge} or if the merge was not in
    *          a {@link CDOBranch#isLocal() local} branch.
-   * @since 4.0
-   * @deprecated Use
-   *             {@link #applyChangeSet(CDOChangeSetData, CDORevisionProvider, CDORevisionProvider, CDOBranchPoint, boolean)}
-   */
-  @Deprecated
-  public Pair<CDOChangeSetData, Pair<Map<CDOID, CDOID>, List<CDOID>>> applyChangeSetData(
-      CDOChangeSetData changeSetData, CDORevisionProvider ancestorProvider, CDORevisionProvider targetProvider,
-      CDOBranchPoint source);
-
-  /**
-   * @param source
-   *          May be <code>null</code> if changeSetData does not result from a
-   *          {@link #merge(CDOBranchPoint, org.eclipse.emf.cdo.transaction.CDOMerger) merge} or if the merge was not in
-   *          a {@link CDOBranch#isLocal() local} branch.
    * @since 4.1
    */
   public ApplyChangeSetResult applyChangeSet(CDOChangeSetData changeSetData, CDORevisionProvider ancestorProvider,
@@ -139,8 +126,39 @@
 
   /**
    * @since 4.0
+   * @deprecated As of 4.3 use {@link #getCleanRevision(InternalCDOObject)}.
    */
-  public Map<InternalCDOObject, InternalCDORevision> getCleanRevisions();
+  @Deprecated
+  public Map<InternalCDOObject, InternalCDORevision> _getCleanRevisions();
+
+  public InternalCDORevision getCleanRevision(CDOObject object);
+
+  /**
+   * @deprecated As of 4.1 use {@link #createIDForNewObject(EObject)}.
+   */
+  @Deprecated
+  public CDOIDTemp getNextTemporaryID();
+
+  /**
+   * @since 4.2
+   * @deprecated As of 4.3 use {@link #updateDirtyState(boolean)}.
+   */
+  @Deprecated
+  public void setDirty(boolean dirty);
+
+  /**
+   * @param source
+   *          May be <code>null</code> if changeSetData does not result from a
+   *          {@link #merge(CDOBranchPoint, org.eclipse.emf.cdo.transaction.CDOMerger) merge} or if the merge was not in
+   *          a {@link CDOBranch#isLocal() local} branch.
+   * @since 4.0
+   * @deprecated Use
+   *             {@link #applyChangeSet(CDOChangeSetData, CDORevisionProvider, CDORevisionProvider, CDOBranchPoint, boolean)}
+   */
+  @Deprecated
+  public Pair<CDOChangeSetData, Pair<Map<CDOID, CDOID>, List<CDOID>>> applyChangeSetData(
+      CDOChangeSetData changeSetData, CDORevisionProvider ancestorProvider, CDORevisionProvider targetProvider,
+      CDOBranchPoint source);
 
   /**
    * Provides a context for a commit operation.
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
index a9b4e72..35e25f2 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
@@ -106,6 +106,8 @@
    */
   public void collectViewedRevisions(Map<CDOID, InternalCDORevision> revisions);
 
+  public void dispatchLoadNotification(InternalCDOObject object);
+
   public void remapObject(CDOID oldID);
 
   /**