예제 #1
0
MergeResult ThreeWayMerge::mergeKeySet (const MergeTask & task)
{

	MergeResult result;
	detectConflicts (task, result);
	detectConflicts (task.reverse (), result, true);

	if (!result.hasConflicts ()) return result;


	// TODO: test this behaviour (would probably need mocks)
	Key current;
	KeySet conflicts = result.getConflictSet ();
	conflicts.rewind ();
	while ((current = conflicts.next ()))
	{
		for (auto & elem : strategies)
		{
			(elem)->resolveConflict (task, current, result);

			if (!result.isConflict (current)) break;
		}
	}

	return result;
}
예제 #2
0
MergeResult ThreeWayMerge::mergeKeySet(const MergeTask& task)
{

	MergeResult result;
	detectConflicts (task, result);
	detectConflicts (task.reverse (), result, true);

	if (!result.hasConflicts()) return result;


	// TODO: test this behaviour (would probably need mocks)
	Key current;
	KeySet conflicts = result.getConflictSet();
	conflicts.rewind();
	while ((current = conflicts.next ()))
	{
		for (vector<MergeConflictStrategy *>::iterator it = strategies.begin (); it != strategies.end (); ++it)
		{
			(*it)->resolveConflict (task, current, result);

			if (!result.isConflict(current))
				break;
		}
	}

	return result;
}
예제 #3
0
TEST (MergeResult, ResolveConflictRemovesKeyFromConflicts)
{
	Key conflictKey = Key ("user/test/config/key1", KEY_VALUE, "testvalue", KEY_END);
	KeySet conflicts;
	conflicts.append (conflictKey);
	KeySet merged;
	MergeResult result (conflicts, merged);

	result.resolveConflict (conflictKey);

	EXPECT_EQ (0, result.getConflictSet ().size ());
}
예제 #4
0
TEST_F(ThreeWayMergeTest, DeleteModifyConflict)
{
	ours.lookup ("user/parento/config/key1", KDB_O_POP);
	theirs.lookup ("user/parentt/config/key1").setString ("modifiedvalue");

	MergeResult result = merger.mergeKeySet (base, ours, theirs, mergeParent);

	EXPECT_TRUE(result.hasConflicts()) << "No conflict detected although conflicts should exist";
	KeySet conflicts = result.getConflictSet ();
	ASSERT_EQ(1, conflicts.size())<< "Wrong number of conflicts";
	testConflictMeta (conflicts.at (0), CONFLICT_DELETE, CONFLICT_MODIFY);
	KeySet merged = result.getMergedKeys ();
	EXPECT_EQ(4, merged.size ());
	compareAllExceptKey1 (merged);
}
예제 #5
0
TEST_F(ThreeWayMergeTest, SameMetaKeyModifyConflict)
{
	ours.lookup ("user/parento/config/key1").setMeta<std::string> ("testmeta", "ourvalue");
	theirs.lookup ("user/parentt/config/key1").setMeta<std::string> ("testmeta", "theirvalue");

	MergeResult result = merger.mergeKeySet (base, ours, theirs, mergeParent);

	ASSERT_TRUE (result.hasConflicts())<< "No conflict detected although conflicts should exist";
	KeySet conflicts = result.getConflictSet ();
	EXPECT_EQ(1, conflicts.size ());
	testConflictMeta (conflicts.at (0), CONFLICT_META, CONFLICT_META);
	KeySet merged = result.getMergedKeys ();
	EXPECT_EQ(4, merged.size ());
	compareAllExceptKey1 (merged);
}
예제 #6
0
TEST_F(ThreeWayMergeTest, SameAddedDifferentValueConflict)
{
	ours.append (Key ("user/parento/config/key5", KEY_VALUE, "newvalueours", KEY_END));
	theirs.append (Key ("user/parentt/config/key5", KEY_VALUE, "newvaluetheirs", KEY_END));

	MergeResult result = merger.mergeKeySet (base, ours, theirs, mergeParent);

	ASSERT_TRUE (result.hasConflicts())<< "No conflict detected although conflicts should exist";

	KeySet conflicts = result.getConflictSet ();
	EXPECT_EQ(1, conflicts.size ());
	testConflictMeta (conflicts.at (0), CONFLICT_ADD, CONFLICT_ADD);

	KeySet merged = result.getMergedKeys ();

	EXPECT_EQ(5, merged.size ());

	compareAllKeys (merged);
}
예제 #7
0
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);
		}
	}
}
예제 #8
0
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;
	}
}