Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include <kdb.hpp>
10 : #include <keysetio.hpp>
11 :
12 : #include <iostream>
13 : #include <memory>
14 : #include <string>
15 :
16 : #include <merging/onesidemergeconfiguration.hpp>
17 : #include <merging/threewaymerge.hpp>
18 :
19 :
20 : using namespace kdb;
21 : using namespace kdb::tools::merging;
22 : using namespace std;
23 :
24 0 : int main ()
25 : {
26 0 : KeySet ours;
27 0 : KeySet theirs;
28 0 : KeySet base;
29 :
30 : // the root of the subtree containing our keys (i.e. our side of the merge)
31 0 : Key oursRoot ("user/ours", KEY_END);
32 :
33 : // the root of the subtree containing their keys (i.e. their side of the merge)
34 0 : Key theirsRoot ("user/theirs", KEY_END);
35 :
36 : // the root of the subtree containing the base keys (i.e. the common ancestor of the merge)
37 0 : Key baseRoot ("user/base", KEY_END);
38 :
39 : // the root of the subtree that will contain the merge result
40 0 : Key resultRoot ("user/result", KEY_END);
41 :
42 : // Step 1: retrieve clean KeySets containing only those
43 : // keys that should be merged. This is a bit trickier than
44 : // it seems at first. Have a look at the documentation of kdbGet
45 : // for detailed information
46 : // things to note:
47 : // * use blocks with local KDB instances so we don't have to worry about
48 : // writing the keys back
49 : // * remove the root key itself from the result KeySet because it usually
50 : // contains the mounted filename and cannot be merged anyway
51 : // Also have a look at the documentation of kdbSet()
52 : // (https://doc.libelektra.org/api/latest/html/group__kdb.html#ga11436b058408f83d303ca5e996832bcf).
53 : // The merging framework can also be used to resolve conflicts resulting from
54 : // concurrent calls to kdbSet() as described in the example of kdbSet().
55 : {
56 0 : KDB lkdb;
57 0 : lkdb.get (ours, oursRoot);
58 0 : ours = ours.cut (oursRoot);
59 0 : ours.lookup (oursRoot, KDB_O_POP);
60 0 : lkdb.get (theirs, theirsRoot);
61 0 : theirs = theirs.cut (theirsRoot);
62 0 : theirs.lookup (theirsRoot, KDB_O_POP);
63 0 : lkdb.get (base, baseRoot);
64 0 : base = base.cut (baseRoot);
65 0 : base.lookup (baseRoot, KDB_O_POP);
66 : }
67 :
68 :
69 : // Step 2: Make sure that no keys reside below the intended merge result root
70 : // Usually the merge can be either aborted if this is the case or the existing
71 : // keys can be overwritten.
72 0 : KeySet resultKeys;
73 0 : kdb::KDB kdb;
74 0 : kdb.get (resultKeys, resultRoot);
75 :
76 0 : KeySet discard = resultKeys.cut (resultRoot);
77 0 : if (discard.size () != 0)
78 : {
79 : // handle existing keys below the result root
80 : return -1;
81 : }
82 :
83 0 : ThreeWayMerge merger;
84 :
85 : // Step 3: Decide which resolution strategies to use. The strategies are registered
86 : // with the merge and applied in order as soon as a conflict is detected. If a strategy
87 : // marks a conflict as resolved, no further strategies are consulted. Therefore the order
88 : // in which they are registered is absolutely crucial. With this chaining the strategies
89 : // remain simple, but can be combined to powerful resolution strategies.
90 : // Have a look at the strategy documentation for further details on what they do and how they work.
91 : // The unit tests also provide insight into how the strategies work.
92 :
93 : // In order to simplify the initialization, predefined merge configurations exist.
94 : // in this example we first resolve all the keys that can be automatically
95 : // resolved (e.g. only one side was modified). This is exactly the use case of the
96 : // AutoMergeConfiguration.
97 :
98 0 : AutoMergeConfiguration configuration;
99 0 : configuration.configureMerger (merger);
100 :
101 : // Step 4: Perform the actual merge
102 : MergeResult result = merger.mergeKeySet (
103 0 : MergeTask (BaseMergeKeys (base, baseRoot), OurMergeKeys (ours, oursRoot), TheirMergeKeys (theirs, theirsRoot), resultRoot));
104 :
105 : // Step 5: work with the result. The merger will return an object containing information
106 : // about the merge result.
107 0 : if (!result.hasConflicts ())
108 : {
109 : // output some statistical information
110 0 : cout << result.getMergedKeys ().size () << " keys in the result" << endl;
111 0 : cout << result.getNumberOfEqualKeys () << " keys were equal" << endl;
112 0 : cout << result.getNumberOfResolvedKeys () << " keys were resolved" << endl;
113 :
114 : // write the result
115 0 : resultKeys.append (result.getMergedKeys ());
116 0 : kdb.set (resultKeys, resultRoot);
117 :
118 : return 0;
119 : }
120 : else
121 : {
122 0 : KeySet conflicts = result.getConflictSet ();
123 :
124 0 : cerr << conflicts.size () << " conflicts were detected that could not be resolved automatically:" << endl;
125 0 : conflicts.rewind ();
126 0 : Key current;
127 0 : while ((current = conflicts.next ()))
128 : {
129 : // For each unresolved conflict there is a conflict key in the merge result.
130 : // This conflict key contains meta information about the reason of the conflict.
131 : // In particular the metakeys conflict/operation/our and conflict/operation/their contain
132 : // the operations done on our version of the key and their version of the key relative to
133 : // the base version of the key.
134 0 : string ourConflict = current.getMeta<string> ("conflict/operation/our");
135 0 : string theirConflict = current.getMeta<string> ("conflict/operation/their");
136 :
137 0 : cerr << current << endl;
138 0 : cerr << "ours: " << ourConflict << ", theirs: " << theirConflict << endl;
139 0 : cerr << endl;
140 : }
141 :
142 0 : cerr << "Merge unsuccessful." << endl;
143 :
144 0 : return -1;
145 : }
146 0 : }
|