Index: src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStore.java	(revision 1437414)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStore.java	(working copy)
@@ -16,25 +16,39 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.google.common.io.ByteStreams;
+import javax.annotation.Nonnull;
+
 import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 
+import com.google.common.io.ByteStreams;
+
 /**
  * Basic in-memory node store implementation. Useful as a base class for
  * more complex functionality.
  */
 public class MemoryNodeStore implements NodeStore {
 
-    private final AtomicReference<NodeState> root =
+    final AtomicReference<NodeState> root =
             new AtomicReference<NodeState>(MemoryNodeState.EMPTY_NODE);
 
+    /**
+     * Commit hook.
+     */
+    @Nonnull
+    private volatile CommitHook hook = EmptyHook.INSTANCE;
+
     @Override
     public NodeState getRoot() {
         return root.get();
@@ -42,7 +56,7 @@
 
     @Override
     public NodeStoreBranch branch() {
-        return new MemoryNodeStoreBranch(root.get());
+        return new MemoryNodeStoreBranch(this, root.get());
     }
 
     /**
@@ -58,15 +72,30 @@
         }
     }
 
-    private class MemoryNodeStoreBranch implements NodeStoreBranch {
+    @Nonnull
+    public CommitHook getHook() {
+        return hook;
+    }
+
+    public void setHook(CommitHook hook) {
+        this.hook = checkNotNull(hook);
+    }
 
+    private static class MemoryNodeStoreBranch implements NodeStoreBranch {
+
+        /** The underlying store to which this branch belongs */
+        private final MemoryNodeStore store;
+
+        /** Root state of the base revision of this branch */
         private final NodeState base;
 
-        private volatile NodeState root;
+        /** Root state of the head revision of this branch*/
+        private volatile NodeState head;
 
-        public MemoryNodeStoreBranch(NodeState base) {
+        public MemoryNodeStoreBranch(MemoryNodeStore store, NodeState base) {
+            this.store = store;
             this.base = base;
-            this.root = base;
+            this.head = base;
         }
 
         @Override
@@ -76,21 +105,27 @@
 
         @Override
         public NodeState getRoot() {
-            return root;
+            checkNotMerged();
+            return head;
         }
 
         @Override
         public void setRoot(NodeState newRoot) {
-            this.root = newRoot;
+            checkNotMerged();
+            this.head = newRoot;
         }
 
         @Override
         public NodeState merge() throws CommitFailedException {
-            while (!MemoryNodeStore.this.root.compareAndSet(base, root)) {
+            checkNotMerged();
+            while (!store.root.compareAndSet(base, head)) {
                 // TODO: rebase();
                 throw new UnsupportedOperationException();
             }
-            return root;
+            NodeState toCommit = store.getHook().processCommit(base, head);
+            store.root.set(toCommit);
+            head = null; // Mark as merged
+            return store.getRoot();
         }
 
         @Override
@@ -103,6 +138,11 @@
             throw new UnsupportedOperationException();
         }
 
+        // ----------------------------------------------------< private >---
+
+        private void checkNotMerged() {
+            checkState(head != null, "Branch has already been merged");
+        }
     }
 
 }
Index: src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java	(revision 1437414)
+++ src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java	(working copy)
@@ -16,7 +16,10 @@
  */
 package org.apache.jackrabbit.oak.security.user;
 
+import java.util.Collections;
+
 import javax.jcr.RepositoryException;
+import javax.security.auth.Subject;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.user.UserManager;
@@ -25,12 +28,23 @@
 import org.apache.jackrabbit.oak.core.RootImpl;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexHookManager;
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
+import org.apache.jackrabbit.oak.plugins.index.p2.Property2IndexHookProvider;
+import org.apache.jackrabbit.oak.plugins.index.p2.Property2IndexProvider;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
 import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
+import org.apache.jackrabbit.oak.spi.query.CompositeQueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.AccessControlConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.OpenAccessControlConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 import org.apache.jackrabbit.oak.util.NodeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -73,21 +87,33 @@
     }
 
     //----------------------------------------------< RepositoryInitializer >---
+
     @Override
-    public void initialize(NodeStore store) {
-        Root root = new RootImpl(store);
+    public NodeState initialize(NodeState state) {
+        MemoryNodeStore ns = new MemoryNodeStore();
+        ns.setHook(IndexHookManager.of(new Property2IndexHookProvider()));
+
+        NodeStoreBranch branch = ns.branch();
+        branch.setRoot(state);
+        try {
+            branch.merge();
+        } catch (CommitFailedException e) {
+            log.error("Failed to initialize user content ", e);
+            throw new RuntimeException(e);
+        }
+        Root root = bootstrapRoot(ns);
+
+        NodeUtil rootTree = new NodeUtil(root.getTree("/"));
+        NodeUtil index = rootTree.getOrAddChild(IndexConstants.INDEX_DEFINITIONS_NAME, JcrConstants.NT_UNSTRUCTURED);
+        IndexUtils.createIndexDefinition(index, "authorizableId", true, UserConstants.REP_AUTHORIZABLE_ID);
+        // FIXME OAK-396: rep:principalName only needs to be unique if defined with user/group nodes -> add defining nt-info to uniqueness constraint otherwise ac-editing will fail.
+        IndexUtils.createIndexDefinition(index, "principalName", true, UserConstants.REP_PRINCIPAL_NAME);
+        IndexUtils.createIndexDefinition(index, "members", false, UserConstants.REP_MEMBERS);
 
         UserConfiguration userConfiguration = securityProvider.getUserConfiguration();
         UserManager userManager = userConfiguration.getUserManager(root, NamePathMapper.DEFAULT);
 
         try {
-            NodeUtil rootTree = new NodeUtil(root.getTree("/"));
-            NodeUtil index = rootTree.getOrAddChild(IndexConstants.INDEX_DEFINITIONS_NAME, JcrConstants.NT_UNSTRUCTURED);
-            IndexUtils.createIndexDefinition(index, "authorizableId", true, UserConstants.REP_AUTHORIZABLE_ID);
-            // FIXME OAK-396: rep:principalName only needs to be unique if defined with user/group nodes -> add defining nt-info to uniqueness constraint otherwise ac-editing will fail.
-            IndexUtils.createIndexDefinition(index, "principalName", true, UserConstants.REP_PRINCIPAL_NAME);
-            IndexUtils.createIndexDefinition(index, "members", false, UserConstants.REP_MEMBERS);
-
             String adminId = userConfiguration.getConfigurationParameters().getConfigValue(PARAM_ADMIN_ID, DEFAULT_ADMIN_ID);
             if (userManager.getAuthorizable(adminId) == null) {
                 // TODO: init admin with null password and force application to set it.
@@ -107,5 +133,18 @@
             log.error("Failed to initialize user content ", e);
             throw new RuntimeException(e);
         }
+        return ns.getRoot();
+    }
+
+    private static Root bootstrapRoot(NodeStore ns) {
+        Subject subject = new Subject(true,
+                Collections.singleton(SystemPrincipal.INSTANCE),
+                Collections.<Object> emptySet(),
+                Collections.<Object> emptySet());
+        AccessControlConfiguration accConfiguration = new OpenAccessControlConfiguration();
+        QueryIndexProvider indexProvider = new CompositeQueryIndexProvider(
+                new Property2IndexProvider());
+        // TODO is passing 'null' for workspace valid here?
+        return new RootImpl(ns, null, subject, accConfiguration, indexProvider);
     }
 }
\ No newline at end of file
