Index: oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/command/MergeCommand.java =================================================================== --- oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/command/MergeCommand.java (revision 1439871) +++ oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/impl/command/MergeCommand.java (working copy) @@ -17,31 +17,21 @@ package org.apache.jackrabbit.mongomk.impl.command; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; -import org.apache.jackrabbit.mk.model.tree.DiffBuilder; -import org.apache.jackrabbit.mk.model.tree.NodeState; import org.apache.jackrabbit.mongomk.api.command.Command; import org.apache.jackrabbit.mongomk.api.model.Commit; -import org.apache.jackrabbit.mongomk.api.model.Node; import org.apache.jackrabbit.mongomk.impl.MongoNodeStore; import org.apache.jackrabbit.mongomk.impl.action.FetchCommitAction; import org.apache.jackrabbit.mongomk.impl.action.FetchCommitsAction; import org.apache.jackrabbit.mongomk.impl.action.FetchHeadRevisionIdAction; +import org.apache.jackrabbit.mongomk.impl.command.exception.ConflictingCommitException; import org.apache.jackrabbit.mongomk.impl.json.JsopParser; import org.apache.jackrabbit.mongomk.impl.json.NormalizingJsopHandler; import org.apache.jackrabbit.mongomk.impl.model.CommitBuilder; import org.apache.jackrabbit.mongomk.impl.model.MongoCommit; -import org.apache.jackrabbit.mongomk.impl.model.NodeImpl; -import org.apache.jackrabbit.mongomk.impl.model.tree.MongoNodeDelta; -import org.apache.jackrabbit.mongomk.impl.model.tree.MongoNodeDelta.Conflict; -import org.apache.jackrabbit.mongomk.impl.model.tree.MongoNodeState; -import org.apache.jackrabbit.mongomk.impl.model.tree.SimpleMongoNodeStore; import org.apache.jackrabbit.mongomk.util.MongoUtil; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +44,7 @@ private final String branchRevisionId; private final String message; + private String conflictingPath; /** * Constructs a {@code MergeCommandMongo} @@ -78,8 +69,6 @@ throw new Exception("Can only merge a private branch commit"); } - long rootNodeId = commit.getRevisionId(); - FetchHeadRevisionIdAction query2 = new FetchHeadRevisionIdAction(nodeStore); long currentHead = query2.execute(); @@ -88,31 +77,13 @@ Commit newCommit; String diff = getNonConflictingCommitsDiff(Math.max(currentHead, commit.getRevisionId()), branchRootId, branchId); - if (diff != null) { - newCommit = CommitBuilder.build("/", diff, - MongoUtil.fromMongoRepresentation(currentHead), message); - } else { - Node ourRoot = getNode("/", rootNodeId, branchId); - - // Merge changes, if any, from trunk to branch. - Node currentHeadNode = getNode("/", currentHead); - if (currentHead != branchRootId) { - ourRoot = mergeNodes(ourRoot, currentHeadNode, branchRootId); - } - - diff = new DiffBuilder(MongoUtil.wrap(currentHeadNode), - MongoUtil.wrap(ourRoot), "/", -1, - new SimpleMongoNodeStore(), "").build(); - - if (diff.isEmpty()) { - LOG.debug("Merge of empty branch {} with differing content hashes encountered, " + - "ignore and keep current head {}", branchRevisionId, currentHead); - return MongoUtil.fromMongoRepresentation(currentHead); - } - - newCommit = CommitBuilder.build("", diff, - MongoUtil.fromMongoRepresentation(currentHead), message); + if (diff == null) { + String message = String.format("Merge @%s: failed due to a conflicting commit." + + " Conflicting path: %s", branchRevisionId, conflictingPath); + throw new ConflictingCommitException(message); } + newCommit = CommitBuilder.build("/", diff, + MongoUtil.fromMongoRepresentation(currentHead), message); Command command = new CommitCommand(nodeStore, newCommit); long revision = command.execute(); @@ -143,11 +114,13 @@ for (String affectedPath : affectedPaths) { if (branchId.equals(commit.getBranchId())) { if (affectedPathsTrunk.contains(affectedPath)) { + conflictingPath = affectedPath; return null; } affectedPathsBranch.add(affectedPath); } else if (commit.getBranchId() == null){ if (affectedPathsBranch.contains(affectedPath)) { + conflictingPath = affectedPath; return null; } affectedPathsTrunk.add(affectedPath); @@ -177,111 +150,4 @@ new JsopParser(path, diff, handler).parse(); return handler.getDiff(); } - - private NodeImpl mergeNodes(Node ourRoot, Node theirRoot, - Long commonAncestorRevisionId) throws Exception { - - Node baseRoot = getNode("/", commonAncestorRevisionId); - Node theirRootCopy = copy(theirRoot); - - // Recursively merge 'our' changes with 'their' changes... - return mergeNode(baseRoot, ourRoot, theirRootCopy, "/"); - } - - private NodeImpl mergeNode(Node baseNode, Node ourNode, Node theirNode, - String path) throws Exception { - MongoNodeDelta theirChanges = new MongoNodeDelta(new SimpleMongoNodeStore(), - MongoUtil.wrap(baseNode), MongoUtil.wrap(theirNode)); - MongoNodeDelta ourChanges = new MongoNodeDelta(new SimpleMongoNodeStore(), - MongoUtil.wrap(baseNode), MongoUtil.wrap(ourNode)); - - NodeImpl stagedNode = (NodeImpl)theirNode; //new NodeImpl(path); - - // Apply our changes. - stagedNode.getProperties().putAll(ourChanges.getAddedProperties()); - stagedNode.getProperties().putAll(ourChanges.getChangedProperties()); - for (String name : ourChanges.getRemovedProperties().keySet()) { - stagedNode.getProperties().remove(name); - } - - for (Map.Entry entry : ourChanges.getAddedChildNodes().entrySet()) { - MongoNodeState nodeState = (MongoNodeState)entry.getValue(); - stagedNode.addChildNodeEntry(nodeState.unwrap()); - } - for (Map.Entry entry : ourChanges.getChangedChildNodes().entrySet()) { - if (!theirChanges.getChangedChildNodes().containsKey(entry.getKey())) { - MongoNodeState nodeState = (MongoNodeState)entry.getValue(); - stagedNode.addChildNodeEntry(nodeState.unwrap()); - } - } - for (String name : ourChanges.getRemovedChildNodes().keySet()) { - stagedNode.removeChildNodeEntry(name); - } - - List conflicts = theirChanges.listConflicts(ourChanges); - // resolve/report merge conflicts - for (Conflict conflict : conflicts) { - String conflictName = conflict.getName(); - String conflictPath = PathUtils.concat(path, conflictName); - switch (conflict.getType()) { - case PROPERTY_VALUE_CONFLICT: - throw new Exception( - "concurrent modification of property " + conflictPath - + " with conflicting values: \"" - + ourNode.getProperties().get(conflictName) - + "\", \"" - + theirNode.getProperties().get(conflictName)); - - case NODE_CONTENT_CONFLICT: { - if (ourChanges.getChangedChildNodes().containsKey(conflictName)) { - // modified subtrees - Node baseChild = baseNode.getChildNodeEntry(conflictName); - Node ourChild = ourNode.getChildNodeEntry(conflictName); - Node theirChild = theirNode.getChildNodeEntry(conflictName); - // merge the dirty subtrees recursively - mergeNode(baseChild, ourChild, theirChild, PathUtils.concat(path, conflictName)); - } else { - // todo handle/merge colliding node creation - throw new Exception("colliding concurrent node creation: " + conflictPath); - } - break; - } - - case REMOVED_DIRTY_PROPERTY_CONFLICT: - stagedNode.getProperties().remove(conflictName); - break; - - case REMOVED_DIRTY_NODE_CONFLICT: - //stagedNode.remove(conflictName); - stagedNode.removeChildNodeEntry(conflictName); - break; - } - - } - return stagedNode; - } - - private Node getNode(String path, long revisionId) throws Exception { - return getNode(path, revisionId, null); - } - - private Node getNode(String path, long revisionId, String branchId) throws Exception { - GetNodesCommand command = new GetNodesCommand(nodeStore, path, revisionId); - command.setBranchId(branchId); - return command.execute(); - } - - private NodeImpl copy(Node node) { - NodeImpl copy = new NodeImpl(node.getPath()); - copy.setRevisionId(node.getRevisionId()); - for (Map.Entry entry : node.getProperties().entrySet()) { - copy.addProperty(entry.getKey(), entry.getValue()); - } - for (Iterator it = node.getChildNodeEntries(0, -1); it.hasNext(); ) { - Node child = it.next(); - copy.addChildNodeEntry(copy(child)); - } - return copy; - } - } \ No newline at end of file