int MergingKDB::synchronize (KeySet & returned, Key & parentKey, ThreeWayMerge & merger) { try { // write our config int ret = KDB::set (returned, parentKey); // update our config (if no conflict) KDB::get (returned, parentKey); return ret; } catch (KDBException const &) { // a conflict occurred, see if we can solve it with the merger // refresh the key database KeySet theirs = returned.dup (); KDB::get (theirs, parentKey); // try to merge MergeResult result = merger.mergeKeySet (MergeTask (BaseMergeKeys (base, parentKey), OurMergeKeys (returned, parentKey), TheirMergeKeys (theirs, parentKey), parentKey)); if (!result.hasConflicts ()) { // hurray, we solved the issue KeySet resultKeys = result.getMergedKeys (); int ret = KDB::set (resultKeys, parentKey); base = resultKeys; return ret; } else { // nothing we can do anymore KeySet conflictSet = result.getConflictSet (); throw MergingKDBException (parentKey, conflictSet); } } }
int ImportCommand::execute (Cmdline const & cl) { size_t argc = cl.arguments.size (); if (argc != 1 && argc != 2 && argc != 3) { throw invalid_argument ("need 1 to 3 arguments"); } Key root = cl.createKey (0); if (!root.isValid ()) { throw invalid_argument ("root key \"" + cl.arguments[0] + "\" is not a valid key name"); } KeySet originalKeys; kdb.get (originalKeys, root); KeySet base = originalKeys.cut (root); printWarnings (cerr, root); string format = cl.format; if (argc > 1) format = cl.arguments[1]; string file = "/dev/stdin"; if (argc > 2 && cl.arguments[2] != "-") file = cl.arguments[2]; Modules modules; PluginPtr plugin = modules.load (format, cl.getPluginsConfig ()); Key errorKey (root); errorKey.setString (file); KeySet importedKeys; plugin->get (importedKeys, errorKey); importedKeys = importedKeys.cut (root); printWarnings (cerr, errorKey); printError (cerr, errorKey); ThreeWayMerge merger; MergeHelper helper; helper.configureMerger (cl, merger); MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (base, root), OurMergeKeys (base, root), TheirMergeKeys (importedKeys, root), root)); helper.reportResult (cl, result, cout, cerr); int ret = -1; if (!result.hasConflicts ()) { if (cl.verbose) { cout << "The merged keyset with strategy " << cl.strategy << " is:" << endl; cout << result.getMergedKeys (); } KeySet resultKeys = result.getMergedKeys (); originalKeys.append (resultKeys); kdb.set (originalKeys, root); ret = 0; } return ret; }
int EditorCommand::execute(Cmdline const& cl) { #ifdef _WIN32 throw EditorNotAvailable(); #endif int argc = cl.arguments.size (); if (argc < 1) { throw invalid_argument ("wrong number of arguments, 1 needed"); } Key root = cl.createKey(0); KeySet ours; KDB kdb; kdb.get (ours, root); KeySet oursToEdit = ours.cut (root); // export it to file string format = cl.format; if (argc > 1) format = cl.arguments[1]; Modules modules; PluginPtr plugin = modules.load(format); tmpFile(); if (cl.verbose) std::cout << "filename set to " << filename << std::endl; Key errorKey(root); errorKey.setString(filename); if (plugin->set(oursToEdit, errorKey) == -1) { printWarnings(cerr, errorKey); printError(cerr, errorKey); return 11; } printWarnings(cerr, errorKey); // start editor if (cl.verbose) std::cout << "running editor with " << filename << std::endl; if (!cl.editor.empty()) { if (!runEditor (cl.editor, filename)) { std::cerr << "Could not run editor " << cl.editor << std::endl; return 12; } } else { if (!runAllEditors(filename)) { std::cerr << "Could not run any editor, please change /sw/elektra/kdb/#0/current/editor" << std::endl; return 12; } } // import from the file KeySet importedKeys; plugin->get(importedKeys, errorKey); importedKeys = importedKeys.cut(root); printWarnings (cerr, errorKey); printError (cerr, errorKey); ThreeWayMerge merger; MergeHelper helper; helper.configureMerger (cl, merger); MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (oursToEdit, root), OurMergeKeys (oursToEdit, root), TheirMergeKeys (importedKeys, root), root)); helper.reportResult (cl, result, cout, cerr); int ret = 13; if (!result.hasConflicts ()) { if (cl.verbose) { cout << "The merged keyset with strategy " << cl.strategy << " is:" << endl; cout << result.getMergedKeys(); } KeySet resultKeys = result.getMergedKeys(); if (cl.verbose) std::cout << "about to write result keys " << resultKeys << std::endl; ours.append(resultKeys); kdb.set (ours, root); if (cl.verbose) std::cout << "successful, cleaning up " << filename << std::endl; unlink(filename.c_str()); ret = 0; } else { std::cout << "Import not successful, please import and remove \"" << filename << '"' << std::endl; } return ret; }
int main() { KeySet ours; KeySet theirs; KeySet base; // the root of the subtree containing our keys (i.e. our side of the merge) Key oursRoot ("user/ours", KEY_END); // the root of the subtree containing their keys (i.e. their side of the merge) Key theirsRoot ("user/theirs", KEY_END); // the root of the subtree containing the base keys (i.e. the common ancestor of the merge) Key baseRoot ("user/base", KEY_END); // the root of the subtree that will contain the merge result Key resultRoot ("user/result", KEY_END); // Step 1: retrieve clean KeySets containing only those // keys that should be merged. This is a bit trickier than // it seems at first. Have a look at the documentation of kdbGet // for detailed information // things to note: // * use blocks with local KDB instances so we don't have to worry about // writing the keys back // * remove the root key itself from the result KeySet because it usually // contains the mounted filename and cannot be merged anyway // Also have a look at the documentation of kdbSet() // (http://doc.libelektra.org/api/latest/html/group__kdb.html#ga11436b058408f83d303ca5e996832bcf). // The merging framework can also be used to resolve conflicts resulting from // concurrent calls to kdbSet() as described in the example of kdbSet(). { KDB lkdb; lkdb.get (ours, oursRoot); ours = ours.cut (oursRoot); ours.lookup(oursRoot, KDB_O_POP); lkdb.get (theirs, theirsRoot); theirs = theirs.cut (theirsRoot); theirs.lookup(theirsRoot, KDB_O_POP); lkdb.get (base, baseRoot); base = base.cut (baseRoot); base.lookup(baseRoot, KDB_O_POP); } // Step 2: Make sure that no keys reside below the intended merge result root // Usually the merge can be either aborted if this is the case or the existing // keys can be overwritten. KeySet resultKeys; kdb::KDB kdb; kdb.get (resultKeys, resultRoot); KeySet discard = resultKeys.cut (resultRoot); if (discard.size () != 0) { // handle existing keys below the result root return -1; } ThreeWayMerge merger; // Step 3: Decide which resolution strategies to use. The strategies are registered // with the merge and applied in order as soon as a conflict is detected. If a strategy // marks a conflict as resolved, no further strategies are consulted. Therefore the order // in which they are registered is absolutely crucial. With this chaining the strategies // remain simple, but can be combined to powerful resolution strategies. // Have a look at the strategy documentation for further details on what they do and how they work. // The unit tests also provide insight into how the strategies work. // In order to simplify the initialization, predefined merge configurations exist. // in this example we first resolve all the keys that can be automatically // resolved (e.g. only one side was modified). This is exactly the use case of the // AutoMergeConfiguration. AutoMergeConfiguration configuration; configuration.configureMerger(merger); // Step 4: Perform the actual merge MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (base, baseRoot), OurMergeKeys (ours, oursRoot), TheirMergeKeys (theirs, theirsRoot), resultRoot)); // Step 5: work with the result. The merger will return an object containing information // about the merge result. if (!result.hasConflicts ()) { // output some statistical information cout << result.getMergedKeys().size() << " keys in the result" << endl; cout << result.getNumberOfEqualKeys() << " keys were equal" << endl; cout << result.getNumberOfResolvedKeys() << " keys were resolved" << endl; // write the result resultKeys.append(result.getMergedKeys()); kdb.set (resultKeys, resultRoot); return 0; } else { KeySet conflicts = result.getConflictSet(); cerr << conflicts.size() << " conflicts were detected that could not be resolved automatically:" << endl; conflicts.rewind(); Key current; while ((current = conflicts.next())) { // For each unresolved conflict there is a conflict key in the merge result. // This conflict key contains meta information about the reason of the conflict. // In particular the metakeys conflict/operation/our and conflict/operation/their contain // the operations done on our version of the key and their version of the key relative to // the base version of the key. string ourConflict = current.getMeta<string> ("conflict/operation/our"); string theirConflict = current.getMeta<string> ("conflict/operation/their"); cerr << current << endl; cerr << "ours: " << ourConflict << ", theirs: " << theirConflict << endl; cerr << endl; } cerr << "Merge unsuccessful." << endl; return -1; } }
int MergeCommand::execute(Cmdline const& cl) { if (cl.arguments.size () < 4) { throw invalid_argument ("wrong number of arguments, 4 needed"); } Key oursRoot = cl.createKey(0); Key theirsRoot = cl.createKey(1); Key baseRoot = cl.createKey(2); Key resultRoot = cl.createKey(3); KeySet ours; KeySet theirs; KeySet base; { KDB lkdb; lkdb.get (ours, oursRoot); ours = ours.cut (oursRoot); ours.lookup(oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got ours: " << oursRoot << " with keys " << ours << std::endl; } { KDB lkdb; lkdb.get (theirs, theirsRoot); theirs = theirs.cut (theirsRoot); ours.lookup(oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got theirs: " << theirsRoot << " with keys " << theirs << std::endl; } { KDB lkdb; lkdb.get (base, baseRoot); base = base.cut (baseRoot); ours.lookup(oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got base: " << baseRoot << " with keys " << base << std::endl; } KeySet resultKeys; kdb.get (resultKeys, resultRoot); KeySet discard = resultKeys.cut (resultRoot); if (discard.size () != 0) { if (cl.force) { if (cl.verbose) { std::cout << "will remove " << discard.size () << " keys, because -f was given" << std::endl; } } else { std::cerr << discard.size () << " keys exist in merge resultroot, will quit. Use -f to override the keys there." << std::endl; } } MergeHelper helper; ThreeWayMerge merger; helper.configureMerger (cl, merger); MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (base, baseRoot), OurMergeKeys (ours, oursRoot), TheirMergeKeys (theirs, theirsRoot), resultRoot)); helper.reportResult (cl, result, cout, cerr); int ret = 0; if (!result.hasConflicts ()) { resultKeys.append(result.getMergedKeys()); kdb.set (resultKeys, resultRoot); } else { ret = -1; } return ret; }