void OneSideStrategy::resolveConflict(const MergeTask& task, Key& conflictKey, MergeResult& result) { string lookupPath; Key winningKey; switch (winningSide) { case BASE: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.baseParent); winningKey = task.base.lookup(lookupPath); break; case OURS: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.ourParent); winningKey = task.ours.lookup(lookupPath); break; case THEIRS: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.theirParent); winningKey = task.theirs.lookup(lookupPath); break; } if (winningKey) { conflictKey.setString(winningKey.getString()); result.resolveConflict(conflictKey); result.addMergeKey(conflictKey); } else { result.resolveConflict(conflictKey); result.removeMergeKey(conflictKey); } }
void NewKeyStrategy::resolveConflict (const MergeTask & task, Key & conflictKey, MergeResult & result) { ConflictOperation ourOperation = getOurConflictOperation (conflictKey); ConflictOperation theirOperation = getTheirConflictOperation (conflictKey); string ourLookup = rebasePath (conflictKey, task.mergeRoot, task.ourParent); string theirLookup = rebasePath (conflictKey, task.mergeRoot, task.theirParent); // TODO: this is a subset of the automergestrategy // the automergestrategy could be split up into several smaller strategies switch (ourOperation) { case CONFLICT_SAME: if (theirOperation == CONFLICT_ADD) { Key source = task.theirs.lookup (theirLookup); copyKeyValue (source, conflictKey); result.resolveConflict (conflictKey); result.addMergeKey (conflictKey); } break; case CONFLICT_ADD: if (theirOperation == CONFLICT_SAME) { Key source = task.ours.lookup (ourLookup); copyKeyValue (source, conflictKey); result.resolveConflict (conflictKey); result.addMergeKey (conflictKey); } break; default: break; } }
void MetaMergeStrategy::resolveConflict(const MergeTask& task, Key& conflictKey, MergeResult& result) { conflictKey.rewindMeta(); Key currentMeta; string baseLookup = rebasePath (conflictKey, task.mergeRoot, task.baseParent); string ourLookup = rebasePath (conflictKey, task.mergeRoot, task.ourParent); string theirLookup = rebasePath (conflictKey, task.mergeRoot, task.theirParent); Key baseKey = task.base.lookup(baseLookup); Key ourKey = task.ours.lookup(ourLookup); Key theirKey = task.theirs.lookup(theirLookup); Key root ("user/", KEY_END); KeySet baseMeta = getMetaKeys (baseKey); KeySet ourMeta = getMetaKeys (ourKey); KeySet theirMeta = getMetaKeys (theirKey); MergeTask metaTask(BaseMergeKeys (baseMeta, root), OurMergeKeys (ourMeta, root), TheirMergeKeys (theirMeta, root), root); MergeResult metaResult = innerMerger.mergeKeySet(metaTask); KeySet mergedMeta = metaResult.getMergedKeys(); Key current; mergedMeta.rewind(); while ((current = mergedMeta.next())) { string metaName = current.getName().substr(string("user/").length()); conflictKey.setMeta(metaName, current.getString()); } ConflictOperation ourOperation = getOurConflictOperation(conflictKey); ConflictOperation theirOperation = getTheirConflictOperation(conflictKey); if (!metaResult.hasConflicts ()) { if (ourOperation == CONFLICT_META && theirOperation == CONFLICT_META) { // TODO: addConflict deletes the key content // without this strategy restoring the value the value would be lost // this happens only for CONFLICT_META <--> CONFLICT_META conflicts // add a test for this behaviour copyKeyValue(ourKey, conflictKey); result.resolveConflict (conflictKey); result.addMergeKey (conflictKey); } } }
Key rebaseKey(const Key& key, const Key& oldParent, const Key& newParent) { string newPath = rebasePath (key, oldParent, newParent); Key result = key.dup (); result.setName (newPath); return result; }
void AutoMergeStrategy::resolveConflict(const MergeTask& task, Key& conflictKey, MergeResult& result) { ConflictOperation ourOperation = getOurConflictOperation(conflictKey); ConflictOperation theirOperation = getTheirConflictOperation(conflictKey); string ourLookup = rebasePath (conflictKey, task.mergeRoot, task.ourParent); string theirLookup = rebasePath (conflictKey, task.mergeRoot, task.theirParent); switch (ourOperation) { case SAME: if (theirOperation == MODIFY || theirOperation == ADD) { Key source = task.theirs.lookup(theirLookup); conflictKey.setString(source.getString()); result.resolveConflict(conflictKey); result.addMergeKey(conflictKey); } if (theirOperation == DELETE) { result.resolveConflict(conflictKey); } break; case MODIFY: case ADD: if (theirOperation == SAME) { Key source = task.ours.lookup(ourLookup); conflictKey.setString(source.getString()); result.resolveConflict(conflictKey); result.addMergeKey(conflictKey); } break; case DELETE: if (theirOperation == SAME) { result.resolveConflict(conflictKey); } break; case META: break; } }
void OneSideValueStrategy::resolveConflict(const MergeTask& task, Key& conflictKey, MergeResult& result) { ConflictOperation ourOperation = getOurConflictOperation (conflictKey); ConflictOperation theirOperation = getTheirConflictOperation (conflictKey); string ourLookup = rebasePath (conflictKey, task.mergeRoot, task.ourParent); string theirLookup = rebasePath (conflictKey, task.mergeRoot, task.theirParent); // TODO: this is a subset of the onesidestrategy // the onesidestrategy could be split up into several smaller strategies if ((ourOperation == CONFLICT_SAME && theirOperation == CONFLICT_MODIFY) || (ourOperation == CONFLICT_MODIFY && theirOperation == CONFLICT_SAME)) { string lookupPath; Key winningKey; switch (winningSide) { case BASE: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.baseParent); winningKey = task.base.lookup (lookupPath); break; case OURS: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.ourParent); winningKey = task.ours.lookup (lookupPath); break; case THEIRS: lookupPath = rebasePath (conflictKey, task.mergeRoot, task.theirParent); winningKey = task.theirs.lookup (lookupPath); break; } if (winningKey) { conflictKey.setString (winningKey.getString ()); result.resolveConflict (conflictKey); result.addMergeKey (conflictKey); } } }
void ThreeWayMerge::detectConflicts (const MergeTask & task, MergeResult & mergeResult, bool reverseConflictMeta = false) { Key our; cursor_t savedCursor = task.ours.getCursor (); task.ours.rewind (); while ((our = task.ours.next ())) { string theirLookup = rebasePath (our, task.ourParent, task.theirParent); Key theirLookupResult = task.theirs.lookup (theirLookup); // we have to copy it to obtain owner etc... Key mergeKey = rebaseKey (our, task.ourParent, task.mergeRoot); if (keyDataEqual (our, theirLookupResult)) { // keydata matches, see if metakeys match if (keyMetaEqual (our, theirLookupResult)) { if (task.ourParent.getFullName () == task.mergeRoot.getFullName ()) { // the key was not rebased, we can reuse our (prevents that the key is rewritten) mergeResult.addMergeKey (our); } else { // the key causes no merge conflict, but the merge result is below a new parent mergeResult.addMergeKey (mergeKey); } } else { // metakeys are different mergeResult.addConflict (mergeKey, CONFLICT_META, CONFLICT_META); } } else { string baseLookup = rebasePath (our, task.ourParent, task.baseParent); Key baseLookupResult = task.base.lookup (baseLookup); // check if the keys was newly added in ours if (baseLookupResult) { // the key exists in base, check if the key still exists in theirs if (theirLookupResult) { // check if only they modified it if (!keyDataEqual (our, baseLookupResult) && keyDataEqual (theirLookupResult, baseLookupResult)) { // the key was only modified in ours addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_MODIFY, CONFLICT_SAME, reverseConflictMeta); } else { // check if both modified it if (!keyDataEqual (our, baseLookupResult) && !keyDataEqual (theirLookupResult, baseLookupResult)) { // the key was modified on both sides mergeResult.addConflict (mergeKey, CONFLICT_MODIFY, CONFLICT_MODIFY); } } } else { // the key does not exist in theirs anymore, check if ours has modified it if (keyDataEqual (our, baseLookupResult)) { // the key was deleted in theirs, and not modified in ours addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_SAME, CONFLICT_DELETE, reverseConflictMeta); } else { // the key was deleted in theirs, but modified in ours addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_MODIFY, CONFLICT_DELETE, reverseConflictMeta); } } } else { // the key does not exist in base, check if the key was added in theirs if (theirLookupResult) { // check if the key was added with the same value in theirs if (keyDataEqual (mergeKey, theirLookupResult)) { if (keyMetaEqual (our, theirLookupResult)) { // the key was added on both sides with the same value if (task.ourParent.getFullName () == task.mergeRoot.getFullName ()) { // the key was not rebased, we can reuse our and prevent the sync flag being // set mergeResult.addMergeKey (our); } else { // the key causes no merge conflict, but the merge result is below a new // parent mergeResult.addMergeKey (mergeKey); } } else { // metakeys are different mergeResult.addConflict (mergeKey, CONFLICT_META, CONFLICT_META); } } else { // the key was added on both sides with different values mergeResult.addConflict (mergeKey, CONFLICT_ADD, CONFLICT_ADD); } } else { // the key was only added to ours addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_ADD, CONFLICT_SAME, reverseConflictMeta); } } } } task.ours.setCursor (savedCursor); }
void ThreeWayMerge::detectConflicts(const MergeTask& task, MergeResult& mergeResult, bool reverseConflictMeta = false) { Key our; cursor_t savedCursor = task.ours.getCursor (); task.ours.rewind (); while ((our = task.ours.next ())) { if (our.getName() == task.ourParent.getName()) continue; string theirLookup = rebasePath (our, task.ourParent, task.theirParent); Key theirLookupResult = task.theirs.lookup (theirLookup); // we have to copy it to obtain owner etc... Key mergeKey = rebaseKey (our, task.ourParent, task.mergeRoot); if (keyDataEqual (our, theirLookupResult)) { // keydata matches, see if metakeys match if (keyMetaEqual (our, theirLookupResult)) { mergeResult.addMergeKey (mergeKey); } else { // metakeys are different mergeResult.addConflict (mergeKey, META, META); } } else { string baseLookup = rebasePath (our, task.ourParent, task.baseParent); Key baseLookupResult = task.base.lookup (baseLookup); // check if the keys was newly added in ours if (baseLookupResult) { // the key exists in base, check if the key still exists in theirs if (theirLookupResult) { // check if only they modified it if (!keyDataEqual (our, baseLookupResult) && keyDataEqual (theirLookupResult, baseLookupResult)) { // the key was only modified in ours addAsymmetricConflict (mergeResult, mergeKey, MODIFY, SAME, reverseConflictMeta); } else { // check if both modified it if (!keyDataEqual (our, baseLookupResult) && !keyDataEqual (theirLookupResult, baseLookupResult)) { // the key was modified on both sides mergeResult.addConflict (mergeKey, MODIFY, MODIFY); } } } else { // the key does not exist in theirs anymore, check if ours has modified it if (keyDataEqual (our, baseLookupResult)) { // the key was deleted in theirs, and not modified in ours addAsymmetricConflict (mergeResult, mergeKey, SAME, DELETE, reverseConflictMeta); } else { // the key was deleted in theirs, but modified in ours addAsymmetricConflict (mergeResult, mergeKey, MODIFY, DELETE, reverseConflictMeta); } } } else { // the key does not exist in base, check if the key was added in theirs if (theirLookupResult) { // check if the key was added with the same value in theirs if (keyDataEqual (mergeKey, theirLookupResult)) { if (keyMetaEqual (our, theirLookupResult)) { // the key was added on both sides with the same value mergeResult.addMergeKey (mergeKey); } else { // metakeys are different mergeResult.addConflict (mergeKey, META, META); } } else { // the key was added on both sides with different values mergeResult.addConflict (mergeKey, ADD, ADD); } } else { // the key was only added to ours addAsymmetricConflict (mergeResult, mergeKey, ADD, SAME, reverseConflictMeta); } } } } task.ours.setCursor (savedCursor); }
void InteractiveMergeStrategy::resolveConflict (const MergeTask & task, Key & conflictKey, MergeResult & result) { ConflictOperation ours = getOurConflictOperation (conflictKey); ConflictOperation theirs = getTheirConflictOperation (conflictKey); outputStream << "merging key " << conflictKey.getName () << endl; outputStream << endl; outputStream << "======== CONFLICT ========" << endl; outputStream << "our operation: " << MergeConflictOperation::getFromTag (ours) << endl; outputStream << "their operation: " << MergeConflictOperation::getFromTag (theirs) << endl; outputStream << endl; Key baseKey = task.base.lookup (rebasePath (conflictKey, task.mergeRoot, task.baseParent)); Key ourKey = task.ours.lookup (rebasePath (conflictKey, task.mergeRoot, task.ourParent)); Key theirKey = task.theirs.lookup (rebasePath (conflictKey, task.mergeRoot, task.theirParent)); outputStream << "======== KEY VALUES ========" << endl; outputKeyInfo ("base", baseKey, outputStream); outputKeyInfo ("ours", ourKey, outputStream); outputKeyInfo ("theirs", theirKey, outputStream); outputStream << endl; char choice; ConflictResolutionSide side; bool repeat; string input; do { outputStream << "What do you want to do?" << endl; outputStream << "Take [o]urs, [t]eirs, [b]ase, [m]erge meta: "; repeat = false; getline (inputStream, input); if (input.size () == 0 || input.size () > 1) { repeat = true; continue; } choice = input.at (0); switch (choice) { case 'o': side = OURS; outputStream << "Choose our key" << endl; break; case 't': side = THEIRS; outputStream << "Choose their key" << endl; break; case 'b': side = BASE; outputStream << "Choose base key" << endl; break; default: repeat = true; } } while (repeat); outputStream << endl; OneSideStrategy strategy (side); strategy.resolveConflict (task, conflictKey, result); outputStream << "Key merged..." << endl; }