Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Implementation of ThreeWayMerge
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include <helper/comparison.hpp>
11 : #include <helper/keyhelper.hpp>
12 : #include <merging/threewaymerge.hpp>
13 :
14 : using namespace std;
15 : using namespace kdb::tools::helper;
16 :
17 : namespace kdb
18 : {
19 :
20 : namespace tools
21 : {
22 :
23 : namespace merging
24 : {
25 :
26 336 : inline void addAsymmetricConflict (MergeResult & result, Key & key, ConflictOperation our, ConflictOperation their, bool reverse)
27 : {
28 336 : if (!reverse)
29 : {
30 63 : result.addConflict (key, our, their);
31 : }
32 : else
33 : {
34 273 : result.addConflict (key, their, our);
35 : }
36 336 : }
37 :
38 868 : void ThreeWayMerge::detectConflicts (const MergeTask & task, MergeResult & mergeResult, bool reverseConflictMeta = false)
39 : {
40 1736 : Key our;
41 1736 : cursor_t savedCursor = task.ours.getCursor ();
42 868 : task.ours.rewind ();
43 :
44 9776 : while ((our = task.ours.next ()))
45 : {
46 1812 : string theirLookup = rebasePath (our, task.ourParent, task.theirParent);
47 2718 : Key theirLookupResult = task.theirs.lookup (theirLookup);
48 :
49 : // we have to copy it to obtain owner etc...
50 1812 : Key mergeKey = rebaseKey (our, task.ourParent, task.mergeRoot);
51 :
52 906 : if (keyDataEqual (our, theirLookupResult))
53 : {
54 : // keydata matches, see if metakeys match
55 510 : if (keyMetaEqual (our, theirLookupResult))
56 : {
57 1518 : if (task.ourParent.getFullName () == task.mergeRoot.getFullName ())
58 : {
59 : // the key was not rebased, we can reuse our (prevents that the key is rewritten)
60 332 : mergeResult.addMergeKey (our);
61 : }
62 : else
63 : {
64 : // the key causes no merge conflict, but the merge result is below a new parent
65 174 : mergeResult.addMergeKey (mergeKey);
66 : }
67 : }
68 : else
69 : {
70 : // metakeys are different
71 4 : mergeResult.addConflict (mergeKey, CONFLICT_META, CONFLICT_META);
72 : }
73 : }
74 : else
75 : {
76 792 : string baseLookup = rebasePath (our, task.ourParent, task.baseParent);
77 1188 : Key baseLookupResult = task.base.lookup (baseLookup);
78 :
79 : // check if the keys was newly added in ours
80 396 : if (baseLookupResult)
81 : {
82 : // the key exists in base, check if the key still exists in theirs
83 165 : if (theirLookupResult)
84 : {
85 : // check if only they modified it
86 104 : if (!keyDataEqual (our, baseLookupResult) && keyDataEqual (theirLookupResult, baseLookupResult))
87 : {
88 : // the key was only modified in ours
89 48 : addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_MODIFY, CONFLICT_SAME,
90 48 : reverseConflictMeta);
91 : }
92 : else
93 : {
94 : // check if both modified it
95 64 : if (!keyDataEqual (our, baseLookupResult) &&
96 8 : !keyDataEqual (theirLookupResult, baseLookupResult))
97 : {
98 : // the key was modified on both sides
99 8 : mergeResult.addConflict (mergeKey, CONFLICT_MODIFY, CONFLICT_MODIFY);
100 : }
101 : }
102 : }
103 : else
104 : {
105 : // the key does not exist in theirs anymore, check if ours has modified it
106 61 : if (keyDataEqual (our, baseLookupResult))
107 : {
108 : // the key was deleted in theirs, and not modified in ours
109 57 : addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_SAME, CONFLICT_DELETE,
110 57 : reverseConflictMeta);
111 : }
112 : else
113 : {
114 : // the key was deleted in theirs, but modified in ours
115 4 : addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_MODIFY, CONFLICT_DELETE,
116 4 : reverseConflictMeta);
117 : }
118 : }
119 : }
120 : else
121 : {
122 : // the key does not exist in base, check if the key was added in theirs
123 231 : if (theirLookupResult)
124 : {
125 : // check if the key was added with the same value in theirs
126 4 : if (keyDataEqual (mergeKey, theirLookupResult))
127 : {
128 0 : if (keyMetaEqual (our, theirLookupResult))
129 : {
130 : // the key was added on both sides with the same value
131 0 : if (task.ourParent.getFullName () == task.mergeRoot.getFullName ())
132 : {
133 : // the key was not rebased, we can reuse our and prevent the sync flag being
134 : // set
135 0 : mergeResult.addMergeKey (our);
136 : }
137 : else
138 : {
139 : // the key causes no merge conflict, but the merge result is below a new
140 : // parent
141 0 : mergeResult.addMergeKey (mergeKey);
142 : }
143 : }
144 : else
145 : {
146 : // metakeys are different
147 0 : mergeResult.addConflict (mergeKey, CONFLICT_META, CONFLICT_META);
148 : }
149 : }
150 : else
151 : {
152 : // the key was added on both sides with different values
153 4 : mergeResult.addConflict (mergeKey, CONFLICT_ADD, CONFLICT_ADD);
154 : }
155 : }
156 : else
157 : {
158 : // the key was only added to ours
159 227 : addAsymmetricConflict (mergeResult, mergeKey, CONFLICT_ADD, CONFLICT_SAME, reverseConflictMeta);
160 : }
161 : }
162 : }
163 : }
164 :
165 1736 : task.ours.setCursor (savedCursor);
166 868 : }
167 :
168 :
169 434 : MergeResult ThreeWayMerge::mergeKeySet (const MergeTask & task)
170 : {
171 :
172 434 : MergeResult result;
173 434 : detectConflicts (task, result);
174 434 : detectConflicts (task.reverse (), result, true);
175 :
176 434 : if (!result.hasConflicts ()) return result;
177 :
178 :
179 : // TODO: test this behaviour (would probably need mocks)
180 106 : Key current;
181 212 : KeySet conflicts = result.getConflictSet ();
182 : conflicts.rewind ();
183 1800 : while ((current = conflicts.next ()))
184 : {
185 1704 : for (auto & elem : strategies)
186 : {
187 658 : (elem)->resolveConflict (task, current, result);
188 :
189 658 : if (!result.isConflict (current)) break;
190 : }
191 : }
192 :
193 : return result;
194 : }
195 :
196 18 : MergeResult ThreeWayMerge::mergeKeySet (const KeySet & base, const KeySet & ours, const KeySet & theirs, const Key & mergeRoot)
197 : {
198 90 : Key ourkey = ours.head ().dup ();
199 90 : Key theirkey = theirs.head ().dup ();
200 90 : Key basekey = base.head ().dup ();
201 :
202 : MergeResult merged = mergeKeySet (
203 108 : MergeTask (BaseMergeKeys (base, basekey), OurMergeKeys (ours, ourkey), TheirMergeKeys (theirs, theirkey), mergeRoot));
204 :
205 18 : return merged;
206 : }
207 : } // namespace merging
208 : } // namespace tools
209 : } // namespace kdb
|