Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/IndexWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/IndexWrapper.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/IndexWrapper.java (date 1356004325000)
@@ -22,6 +22,8 @@
import java.util.Iterator;
import java.util.Set;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.json.JsopReader;
@@ -113,6 +115,12 @@
branchRevisions.remove(branchRevisionId);
indexer.updateUntil(headRevision);
return mk.getHeadRevision();
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
@Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/simple/SimpleKernelImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/simple/SimpleKernelImpl.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/simple/SimpleKernelImpl.java (date 1356004325000)
@@ -16,6 +16,13 @@
*/
package org.apache.jackrabbit.oak.plugins.index.old.mk.simple;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.blobs.AbstractBlobStore;
@@ -32,11 +39,6 @@
import org.apache.jackrabbit.oak.plugins.index.old.mk.ExceptionFactory;
import org.apache.jackrabbit.oak.plugins.index.old.mk.wrapper.MicroKernelWrapperBase;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-
/*
Node structure:
@@ -610,6 +612,12 @@
@Override
public String merge(String branchRevisionId, String message) throws MicroKernelException {
// TODO OAK-45 support
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
throw new UnsupportedOperationException();
}
}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/BranchMergeMicroKernel.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/BranchMergeMicroKernel.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/BranchMergeMicroKernel.java (date 1356004325000)
@@ -18,6 +18,9 @@
import java.io.InputStream;
import java.util.HashSet;
+
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
@@ -184,6 +187,12 @@
busyBranch = null;
trunkHeadRevision = null;
return getHeadRevision();
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
@Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/LogWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/LogWrapper.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/LogWrapper.java (date 1356004325000)
@@ -19,6 +19,8 @@
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.oak.plugins.index.old.mk.ExceptionFactory;
@@ -225,6 +227,12 @@
logException(e);
throw convert(e);
}
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
private void logMethod(String methodName, Object... args) {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/MicroKernelWrapperBase.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/MicroKernelWrapperBase.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/MicroKernelWrapperBase.java (date 1356004325000)
@@ -17,6 +17,9 @@
package org.apache.jackrabbit.oak.plugins.index.old.mk.wrapper;
import java.io.InputStream;
+
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.json.JsopReader;
@@ -148,6 +151,12 @@
@Override
public String merge(String branchRevisionId, String message) {
return wrapped.merge(branchRevisionId, message);
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
@Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/SecurityWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/SecurityWrapper.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/SecurityWrapper.java (date 1356004325000)
@@ -16,6 +16,10 @@
*/
package org.apache.jackrabbit.oak.plugins.index.old.mk.wrapper;
+import java.io.InputStream;
+
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.json.JsopReader;
import org.apache.jackrabbit.mk.json.JsopStream;
@@ -27,8 +31,6 @@
import org.apache.jackrabbit.oak.plugins.index.old.mk.simple.NodeImpl;
import org.apache.jackrabbit.oak.plugins.index.old.mk.simple.NodeMap;
-import java.io.InputStream;
-
/**
* A microkernel prototype implementation that filters nodes based on simple
* access rights. Each user has a password, and (optionally) a list of rights,
@@ -358,6 +360,12 @@
public String merge(String branchRevisionId, String message) {
// TODO OAK-45 support
return mk.merge(branchRevisionId, message);
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
private NodeImpl filterAccess(String path, NodeImpl n) {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/TimingWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/TimingWrapper.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/TimingWrapper.java (date 1356004325000)
@@ -22,6 +22,8 @@
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.oak.plugins.index.old.mk.ExceptionFactory;
@@ -288,6 +290,12 @@
logException(e);
throw convert(e);
}
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
private void logMethod(String methodName, Object... args) {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/VirtualRepositoryWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/VirtualRepositoryWrapper.java (date 1357827292000)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/mk/wrapper/VirtualRepositoryWrapper.java (date 1356004325000)
@@ -21,6 +21,8 @@
import java.util.TreeMap;
import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.json.JsopBuilder;
@@ -343,6 +345,12 @@
public String merge(String branchRevisionId, String message) {
// TODO OAK-45 support
return mk.merge(branchRevisionId, message);
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
}
Index: oak-mk-api/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk-api/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java (date 1357827292000)
+++ oak-mk-api/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java (date 1356004325000)
@@ -18,6 +18,8 @@
import java.io.InputStream;
+import javax.annotation.Nonnull;
+
/**
* The MicroKernel Design Goals and Principles:
*
@@ -480,6 +482,27 @@
*/
String /* revisionId */ merge(String branchRevisionId, String message)
throws MicroKernelException;
+
+ /**
+ * Rebases the specified private branch revision on top of specified new base
+ * revision.
+ *
+ * A {@code MicroKernelException} is thrown if {@code branchRevisionId} doesn't
+ * exist, if it's not a branch revision, if {@code newBaseRevisionId} doesn't exist,
+ * if it's a branch revision, if the rebase fails because of conflicting changes or
+ * if another error occurs.
+ *
+ * @param branchRevisionId id of private branch revision
+ * @param newBaseRevisionId id of new base revision
+ * @return id of the rebased branch revision
+ * @throws MicroKernelException if {@code branchRevisionId} doesn't exist,
+ * if it's not a branch revision, if {@code newBaseRevisionId}
+ * doesn't exist, if it's a branch revision,
+ * if the rebase fails because of conflicting changes or
+ * if another error occurs.
+ */
+ @Nonnull
+ String /*revisionId */ rebase(@Nonnull String branchRevisionId, String newBaseRevisionId);
//--------------------------------------------------< BLOB READ/WRITE ops >
Index: oak-mk-remote/src/main/java/org/apache/jackrabbit/mk/client/Client.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk-remote/src/main/java/org/apache/jackrabbit/mk/client/Client.java (date 1357827292000)
+++ oak-mk-remote/src/main/java/org/apache/jackrabbit/mk/client/Client.java (date 1356004325000)
@@ -23,6 +23,7 @@
import java.net.URISyntaxException;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.Nonnull;
import javax.net.SocketFactory;
import org.apache.jackrabbit.mk.api.MicroKernel;
@@ -297,6 +298,12 @@
} finally {
IOUtils.closeQuietly(request);
}
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
@Override
Index: oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java (date 1357827292000)
+++ oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java (date 1356004325000)
@@ -16,15 +16,19 @@
*/
package org.apache.jackrabbit.mk.core;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.mk.json.JsonObject;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.mk.json.JsopReader;
import org.apache.jackrabbit.mk.json.JsopTokenizer;
import org.apache.jackrabbit.mk.model.Commit;
import org.apache.jackrabbit.mk.model.CommitBuilder;
import org.apache.jackrabbit.mk.model.Id;
-import org.apache.jackrabbit.mk.json.JsonObject;
import org.apache.jackrabbit.mk.model.StoredCommit;
import org.apache.jackrabbit.mk.model.tree.ChildNode;
import org.apache.jackrabbit.mk.model.tree.DiffBuilder;
@@ -38,10 +42,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
/**
*
*/
@@ -124,6 +124,15 @@
}
}
+ private Id getBaseRevisionId(Id branchId) throws MicroKernelException {
+ try {
+ return rep.getBaseRevision(branchId);
+ }
+ catch (Exception e) {
+ throw new MicroKernelException(e);
+ }
+ }
+
public String getRevisionHistory(long since, int maxEntries, String path) throws MicroKernelException {
if (rep == null) {
throw new IllegalStateException("this instance has already been disposed");
@@ -521,6 +530,28 @@
return cb.doCommit(true).toString();
} catch (Exception e) {
throw new MicroKernelException(e);
+ }
+ }
+
+ @Override
+ public String rebase(String branchRevisionId, String newBaseRevisionId) {
+ Id branchId = Id.fromString(branchRevisionId);
+ Id baseId = getBaseRevisionId(branchId);
+ Id newBaseId = newBaseRevisionId == null ? getHeadRevisionId() : Id.fromString(newBaseRevisionId);
+
+ if (baseId.equals(newBaseId)) {
+ return branchRevisionId;
+ }
+ else {
+ Id newBranchId = Id.fromString(branch(newBaseRevisionId));
+ try {
+ CommitBuilder cb = rep.getCommitBuilder(newBranchId,
+ "rebasing " + branchRevisionId + " onto " + newBaseRevisionId);
+ return cb.rebase(baseId, branchId).toString();
+ }
+ catch (Exception e) {
+ throw new MicroKernelException(e);
+ }
}
}
Index: oak-mk/src/main/java/org/apache/jackrabbit/mk/core/Repository.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk/src/main/java/org/apache/jackrabbit/mk/core/Repository.java (date 1357827292000)
+++ oak-mk/src/main/java/org/apache/jackrabbit/mk/core/Repository.java (date 1356004325000)
@@ -16,6 +16,9 @@
*/
package org.apache.jackrabbit.mk.core;
+import java.io.Closeable;
+import java.io.File;
+
import org.apache.jackrabbit.mk.blobs.BlobStore;
import org.apache.jackrabbit.mk.blobs.FileBlobStore;
import org.apache.jackrabbit.mk.blobs.MemoryBlobStore;
@@ -31,9 +34,6 @@
import org.apache.jackrabbit.mk.util.IOUtils;
import org.apache.jackrabbit.oak.commons.PathUtils;
-import java.io.Closeable;
-import java.io.File;
-
/**
*
*/
@@ -142,6 +142,14 @@
throw new IllegalStateException("not initialized");
}
return rs.getHeadCommitId();
+ }
+
+ public Id getBaseRevision(Id branchRevision) throws Exception {
+ if (!initialized) {
+ throw new IllegalStateException("not initialized");
+ }
+ StoredCommit commit = rs.getCommit(branchRevision);
+ return commit == null ? null : commit.getBranchRootId();
}
public StoredCommit getHeadCommit() throws Exception {
Index: oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java (date 1357827292000)
+++ oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java (date 1356004325000)
@@ -16,6 +16,9 @@
*/
package org.apache.jackrabbit.mk.model;
+import java.util.ArrayList;
+import java.util.List;
+
import org.apache.jackrabbit.mk.json.JsonObject;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.mk.model.tree.DiffBuilder;
@@ -25,9 +28,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-
/**
*
*/
@@ -186,6 +186,39 @@
}
newRevId = store.putCommit(token, newCommit);
}
+
+ // reset instance
+ stagedTree.reset(newRevId);
+ changeLog.clear();
+
+ return newRevId;
+ }
+
+ public Id rebase(Id fromId, Id toId) throws Exception {
+ RevisionStore.PutToken token = store.createPutToken();
+
+ Id rebasedId = stagedTree.rebase(baseRevId, fromId, toId, token);
+
+ if (store.getCommit(toId).getRootNodeId().equals(rebasedId)) {
+ // the rebase didn't cause any changes,
+ // no need to create new commit object/update head revision
+ return toId;
+ }
+
+ StoredCommit baseCommit = store.getCommit(baseRevId);
+ MutableCommit newCommit = new MutableCommit();
+ newCommit.setParentId(baseRevId);
+ newCommit.setCommitTS(System.currentTimeMillis());
+ newCommit.setMsg(msg);
+ // dynamically build diff for rebased commit
+ String diff = new DiffBuilder(
+ store.getNodeState(store.getRootNode(toId)),
+ store.getNodeState(store.getNode(rebasedId)),
+ "/", -1, store, "").build();
+ newCommit.setChanges(diff);
+ newCommit.setRootNodeId(rebasedId);
+ newCommit.setBranchRootId(baseCommit.getBranchRootId());
+ Id newRevId = store.putCommit(token, newCommit);
// reset instance
stagedTree.reset(newRevId);
Index: oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StagedNodeTree.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StagedNodeTree.java (date 1357827292000)
+++ oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StagedNodeTree.java (date 1356004325000)
@@ -16,16 +16,20 @@
*/
package org.apache.jackrabbit.mk.model;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
import org.apache.jackrabbit.mk.json.JsonObject;
import org.apache.jackrabbit.mk.model.tree.NodeDelta;
+import org.apache.jackrabbit.mk.model.tree.NodeState;
+import org.apache.jackrabbit.mk.model.tree.PropertyState;
import org.apache.jackrabbit.mk.store.NotFoundException;
import org.apache.jackrabbit.mk.store.RevisionStore;
+import org.apache.jackrabbit.mk.store.RevisionStore.PutToken;
import org.apache.jackrabbit.oak.commons.PathUtils;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
/**
* A {@code StagedNodeTree} provides methods to manipulate a specific revision
* of the tree. The changes are recorded and can be persisted by calling
@@ -80,6 +84,21 @@
return root != null ? root.persist(token) : null;
}
+ public Id rebase(Id baseId, Id fromId, Id toId, PutToken token) throws Exception {
+ // reset staging area to new base revision
+ reset(baseId);
+
+ StoredNode baseRoot = store.getRootNode(baseId);
+ StoredNode fromRoot = store.getRootNode(fromId);
+ StoredNode toRoot = store.getRootNode(toId);
+
+ // recursively apply changes from 'fromRoot' to 'toRoot' to 'baseRoot'
+ rebaseNode(baseRoot, fromRoot, toRoot, "/");
+
+ // persist staged nodes
+ return persist(token);
+ }
+
/**
* Performs a three-way merge merging our tree (rooted at {@code ourRoot})
* and their tree (identified by {@code newBaseRevisionId}),
@@ -360,6 +379,106 @@
parent = node;
}
return node;
+ }
+
+ private void rebaseNode(StoredNode base, StoredNode from, StoredNode to, String path) throws Exception {
+ assert from != null;
+ assert to != null;
+ assert base != null;
+
+ NodeState baseState = store.getNodeState(base);
+ NodeDelta theirDelta = new NodeDelta(store, store.getNodeState(from), store.getNodeState(base));
+ NodeDelta delta = new NodeDelta(store, store.getNodeState(from), store.getNodeState(to));
+
+ // apply the changes
+ StagedNode stagedNode = getStagedNode(path, true);
+
+ for (Entry added : delta.getAddedProperties().entrySet()) {
+ String name = added.getKey();
+ String value = added.getValue();
+
+ PropertyState p = baseState.getProperty(name);
+ if (p == null || value.equals(p.getEncodedValue())) {
+ stagedNode.getProperties().put(name, value);
+ }
+ else {
+ throw new Exception("Added property conflicts with existing property '" + name + "' at '" + path + '\'');
+ }
+ }
+
+ for (String removed : delta.getRemovedProperties().keySet()) {
+ if (baseState.getProperty(removed) != null) {
+ if (theirDelta.getAddedProperties().containsKey(removed)) {
+ throw new Exception("Removed property conflicts with added property '" + removed + "' at '" + path + '\'');
+ }
+ if (theirDelta.getChangedProperties().containsKey(removed)) {
+ throw new Exception("Removed property conflicts with changed property '" + removed + "' at '" + path + '\'');
+ }
+ stagedNode.getProperties().remove(removed);
+ }
+ }
+
+ for (Entry changed : delta.getChangedProperties().entrySet()) {
+ String name = changed.getKey();
+ String value = changed.getValue();
+
+ PropertyState p = baseState.getProperty(name);
+ if (p == null) {
+ throw new Exception("Changed property conflicts with removed property '" + name + "' at '" + path + '\'');
+ }
+ else {
+ if (theirDelta.getAddedProperties().containsKey(name)) {
+ throw new Exception("Changed property conflicts with added property '" + name + "' at '" + path + '\'');
+ }
+ String theirs = theirDelta.getChangedProperties().get(name);
+ if (theirs != null && !theirs.equals(value)) {
+ throw new Exception("Changes property conflicts with changed property '" + name + "' at '" + path + '\'');
+ }
+ stagedNode.getProperties().put(name, value);
+ }
+ }
+
+ for (Entry added : delta.getAddedChildNodes().entrySet()) {
+ String name = added.getKey();
+ Id id = added.getValue();
+
+ NodeState n = baseState.getChildNode(name);
+ if (n == null) {
+ stagedNode.add(new ChildNodeEntry(name, id));
+ }
+ else {
+ throw new Exception("Added node conflicts with existing node '" + name + "' at '" + path + '\'');
+ }
+ }
+
+ for (String removed : delta.getRemovedChildNodes().keySet()) {
+ if (baseState.getChildNode(removed) != null) {
+ if (theirDelta.getAddedChildNodes().containsKey(removed)) {
+ throw new Exception("Removed node conflicts with added node '" + removed + "' at '" + path + '\'');
+ }
+ if (theirDelta.getChangedChildNodes().containsKey(removed)) {
+ throw new Exception("Removed node conflicts with changed node '" + removed + "' at '" + path + '\'');
+ }
+ stagedNode.remove(removed);
+ }
+ }
+
+ for (String changed : delta.getChangedChildNodes().keySet()) {
+ StoredNode changedBase = getChildNode(base, changed);
+ if (changedBase == null) {
+ throw new Exception("Changed node conflicts with removed node '" + changed + "' at '" + path + '\'');
+ }
+
+ StoredNode changedFrom = getChildNode(from, changed);
+ StoredNode changedTo = getChildNode(to, changed);
+ String changedPath = PathUtils.concat(path, changed);
+ rebaseNode(changedBase, changedFrom, changedTo, changedPath);
+ }
+ }
+
+ private StoredNode getChildNode(StoredNode parent, String name) throws Exception {
+ ChildNodeEntry cne = parent.getChildNodeEntry(name);
+ return cne == null ? null : store.getNode(cne.getId());
}
/**
Index: oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java (date 1357827292000)
+++ oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java (date 1356004325000)
@@ -19,6 +19,7 @@
import java.io.File;
import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.core.MicroKernelImpl;
import org.junit.After;
import org.junit.Before;
@@ -26,6 +27,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class MicroKernelImplTest {
@@ -63,4 +68,316 @@
headRev = mk.commit("/", "+\"b\" : {}", mk.getHeadRevision(), null);
assertFalse("Commit must not have same id as branch", headRev.equals(branchRev));
}
+
+ @Test
+ public void rebaseWithoutChanges() {
+ String branch = mk.branch(null);
+ String rebased = mk.rebase(branch, null);
+ assertEquals(branch, rebased);
+ }
+
+ @Test
+ public void fastForwardRebase() {
+ String branch = mk.branch(null);
+ branch = mk.commit("", "+\"/a\":{}", branch, null);
+ String rebased = mk.rebase(branch, null);
+ assertEquals(branch, rebased);
+ }
+
+ @Test
+ public void rebaseEmptyBranch() {
+ String branch = mk.branch(null);
+ String trunkRev = mk.commit("", "+\"/a\":{}", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ assertEquals("{\":childNodeCount\":1,\"a\":{}}", mk.getNodes("/", rebased, 0, 0, -1, null));
+ assertEquals("{\":childNodeCount\":1,\"a\":{}}", mk.getNodes("/", null, 0, 0, -1, null));
+ assertEquals(trunkRev, mk.getHeadRevision());
+ assertFalse((trunkRev.equals(rebased)));
+ }
+
+ @Test
+ public void rebaseAddNode() {
+ mk.commit("", "+\"/x\":{}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "+\"/x/b\":{}", branch, null);
+ String trunkRev = mk.commit("", "+\"/x/a\":{}", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ assertEquals(1, mk.getChildNodeCount("/x", null));
+ assertNotNull(mk.getNodes("/x/a", null, 0, 0, -1, null));
+
+ assertEquals(1, mk.getChildNodeCount("/x", branch));
+ assertNotNull(mk.getNodes("/x/b", branch, 0, 0, -1, null));
+
+ assertEquals(2, mk.getChildNodeCount("/x", rebased));
+ assertNotNull(mk.getNodes("/x/a", rebased, 0, 0, -1, null));
+ assertNotNull(mk.getNodes("/x/b", rebased, 0, 0, -1, null));
+ }
+
+ @Test
+ public void rebaseRemoveNode() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "-\"/x/y\"", branch, null);
+ String trunkRev = mk.commit("", "+\"/x/a\":{}", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ assertEquals(2, mk.getChildNodeCount("/x", null));
+ assertNotNull(mk.getNodes("/x/a", null, 0, 0, -1, null));
+ assertNotNull(mk.getNodes("/x/y", null, 0, 0, -1, null));
+
+ assertEquals(0, mk.getChildNodeCount("/x", branch));
+
+ assertEquals(1, mk.getChildNodeCount("/x", rebased));
+ assertNotNull(mk.getNodes("/x/a", rebased, 0, 0, -1, null));
+ }
+
+ @Test
+ public void rebaseAddProperty() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":42", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/q\":99", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":42"));
+ assertFalse(branchNode.contains("\"q\":99"));
+
+ String rebasedNode = mk.getNodes("/x/y", rebased, 0, 0, -1, null);
+ assertTrue(rebasedNode.contains("\"p\":42"));
+ assertTrue(rebasedNode.contains("\"q\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertFalse(trunkNode.contains("\"p\":42"));
+ assertTrue(trunkNode.contains("\"q\":99"));
+ }
+
+ @Test
+ public void rebaseRemoveProperty() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":null", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/q\":99", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertFalse(branchNode.contains("\"p\":42"));
+ assertFalse(branchNode.contains("\"q\":99"));
+
+ String rebasedNode = mk.getNodes("/x/y", rebased, 0, 0, -1, null);
+ assertFalse(rebasedNode.contains("\"p\":42"));
+ assertTrue(rebasedNode.contains("\"q\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":42"));
+ assertTrue(trunkNode.contains("\"q\":99"));
+ }
+
+ @Test
+ public void rebaseChangeProperty() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":41", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/q\":99", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":41"));
+ assertFalse(branchNode.contains("\"q\":99"));
+
+ String rebasedNode = mk.getNodes("/x/y", rebased, 0, 0, -1, null);
+ assertTrue(rebasedNode.contains("\"p\":41"));
+ assertTrue(rebasedNode.contains("\"q\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":42"));
+ assertTrue(trunkNode.contains("\"q\":99"));
+ }
+
+ @Test
+ public void rebaseChangePropertyWithSameValue() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":99", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":99", null, null);
+ String rebased = mk.rebase(branch, null);
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":99"));
+
+ String rebasedNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(rebasedNode.contains("\"p\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":99"));
+ }
+
+ @Test
+ public void rebaseAddNodeConflict() {
+ mk.commit("", "+\"/x\":{}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "+\"/x/a\":{}", branch, null);
+ String trunkRev = mk.commit("", "+\"/x/a\":{}", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/a'");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ assertEquals(1, mk.getChildNodeCount("/x", null));
+ assertNotNull(mk.getNodes("/x/a", null, 0, 0, -1, null));
+
+ assertEquals(1, mk.getChildNodeCount("/x", branch));
+ assertNotNull(mk.getNodes("/x/a", branch, 0, 0, -1, null));
+ }
+
+ @Test
+ public void rebaseAddPropertyConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":42", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":99", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/y/p'");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":42"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":99"));
+ }
+
+ @Test
+ public void rebaseRemovePropertyConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":99", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":null", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/y/p'");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertFalse(trunkNode.contains("\"p\":42"));
+ assertFalse(trunkNode.contains("\"p\":99"));
+ }
+
+ @Test
+ public void rebaseRemoveChangedPropertyConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":null", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":99", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/y/p'");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertFalse(branchNode.contains("\"p\":42"));
+ assertFalse(branchNode.contains("\"p\":99"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":99"));
+ }
+
+ @Test
+ public void rebaseChangedChangedPropertyConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{\"p\":42}}", null, null);
+ String branch = mk.branch(null);
+ branch = mk.commit("", "^\"/x/y/p\":41", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":99", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/y/p'");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ String branchNode = mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":41"));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":99"));
+ }
+
+ @Test
+ public void rebaseRemoveChangedNodeConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ mk.getNodes("/x/y", branch, 0, 0, -1, null);
+ branch = mk.commit("", "-\"/x/y\"", branch, null);
+ String trunkRev = mk.commit("", "^\"/x/y/p\":42", null, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x/y");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ assertFalse(mk.nodeExists("/x/y", branch));
+
+ String trunkNode = mk.getNodes("/x/y", null, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":42"));
+ }
+
+ @Test
+ public void rebaseChangeRemovedNodeConflict() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ String trunkRev = mk.commit("", "-\"/x\"", null, null);
+ branch = mk.commit("", "^\"/x/p\":42", branch, null);
+ try {
+ mk.rebase(branch, null);
+ fail("Expected MicroKernelException for conflict at '/x");
+ }
+ catch (MicroKernelException e) {
+ assertSame(Exception.class, e.getCause().getClass());
+ }
+
+ assertFalse(mk.nodeExists("/x", null));
+
+ String branchNode = mk.getNodes("/x", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":42"));
+ }
+
+ @Test
+ public void mergeRebased() {
+ mk.commit("", "+\"/x\":{\"y\":{}}", null, null);
+ String branch = mk.branch(null);
+ String trunkRev = mk.commit("", "^\"/x/p\":42", null, null);
+ branch = mk.commit("", "^\"/x/q\":43", branch, null);
+ branch = mk.rebase(branch, null);
+
+ String branchNode = mk.getNodes("/x", branch, 0, 0, -1, null);
+ assertTrue(branchNode.contains("\"p\":42"));
+ assertTrue(branchNode.contains("\"q\":43"));
+
+ mk.merge(branch, null);
+ String trunkNode = mk.getNodes("/x", branch, 0, 0, -1, null);
+ assertTrue(trunkNode.contains("\"p\":42"));
+ assertTrue(trunkNode.contains("\"q\":43"));
+ }
+
}
Index: oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/MongoMicroKernel.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/MongoMicroKernel.java (date 1357827292000)
+++ oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/MongoMicroKernel.java (date 1356004325000)
@@ -19,6 +19,8 @@
import java.io.InputStream;
import java.util.UUID;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.blobs.BlobStore;
@@ -213,6 +215,12 @@
} catch (Exception e) {
throw new MicroKernelException(e);
}
+ }
+
+ @Nonnull
+ @Override
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId) {
+ throw new UnsupportedOperationException();
}
@Override
\ No newline at end of file