monotone

monotone Mtn Source Tree

Root/src/merge_roster.cc

1// Copyright (C) 2008, 2010, 2012 Stephen Leake <stephen_leake@stephe-leake.org>
2// 2005 Nathaniel Smith <njs@pobox.com>
3//
4// This program is made available under the GNU GPL version 2.0 or
5// greater. See the accompanying file COPYING for details.
6//
7// This program is distributed WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9// PURPOSE.
10
11#include "base.hh"
12#include "merge_roster.hh"
13
14#include "sanity.hh"
15#include "safe_map.hh"
16#include "parallel_iter.hh"
17#include "vocab_cast.hh"
18
19#include <sstream>
20
21using std::make_pair;
22using std::pair;
23using std::set;
24using std::string;
25using std::ostringstream;
26
27enum side_t {left_side, right_side};
28
29namespace resolve_conflicts
30{
31 char const *
32 image(side_t item)
33 {
34 switch (item)
35 {
36 case resolve_conflicts::left_side:
37 return "left_side";
38 case resolve_conflicts::right_side:
39 return "right_side";
40 }
41 I(false); // keep compiler happy
42 }
43
44 char const *
45 image(resolution_t resolution)
46 {
47 switch (resolution)
48 {
49 case resolve_conflicts::none:
50 return "none";
51 case resolve_conflicts::content_user:
52 return "content_user";
53 case resolve_conflicts::content_internal:
54 return "content_internal";
55 case resolve_conflicts::drop:
56 return "drop";
57 case resolve_conflicts::keep:
58 return "keep";
59 case resolve_conflicts::rename:
60 return "rename";
61 case resolve_conflicts::content_user_rename:
62 return "content_user_rename";
63 }
64 I(false); // keep compiler happy
65 }
66
67 string
68 image(file_resolution_t res)
69 {
70 if (res.resolution == resolve_conflicts::none)
71 return string("\n");
72 else
73 {
74 ostringstream oss;
75 oss << "resolution: " << image(res.resolution);
76 if (res.content != 0)
77 oss << ", content: '" << res.content->as_external() << "'";
78 if (res.rename.as_internal().length()>0)
79 oss << ", rename: '" << res.rename.as_external() << "'";
80 oss << "\n";
81 return oss.str();
82 }
83 }
84}
85
86template <> void
87dump(invalid_name_conflict const & conflict, string & out)
88{
89 ostringstream oss;
90 oss << "invalid_name_conflict on node: " << conflict.nid << " "
91 << "parent: " << conflict.parent_name.first << " "
92 << "basename: " << conflict.parent_name.second << "\n";
93 out = oss.str();
94}
95
96template <> void
97dump(directory_loop_conflict const & conflict, string & out)
98{
99 ostringstream oss;
100 oss << "directory_loop_conflict on node: " << conflict.nid << " "
101 << "parent: " << conflict.parent_name.first << " "
102 << "basename: " << conflict.parent_name.second << "\n";
103 out = oss.str();
104}
105
106template <> void
107dump(orphaned_node_conflict const & conflict, string & out)
108{
109 ostringstream oss;
110 oss << "orphaned_node_conflict on node: " << conflict.nid << " "
111 << "parent: " << conflict.parent_name.first << " "
112 << "basename: " << conflict.parent_name.second << "\n";
113 out = oss.str();
114}
115
116template <> void
117dump(multiple_name_conflict const & conflict, string & out)
118{
119 ostringstream oss;
120 oss << "multiple_name_conflict on node: " << conflict.nid << " "
121 << "left parent: " << conflict.left.first << " "
122 << "basename: " << conflict.left.second << " "
123 << "right parent: " << conflict.right.first << " "
124 << "basename: " << conflict.right.second << "\n";
125 out = oss.str();
126}
127
128template <> void
129dump(dropped_modified_conflict const & conflict, string & out)
130{
131 ostringstream oss;
132 oss << "dropped_modified_conflict -\n";
133 oss << " dropped_side : " << image(conflict.dropped_side) << "\n";
134 oss << " left_nid : " << conflict.left_nid << "\n";
135 oss << " right_nid : " << conflict.right_nid << "\n";
136 oss << " orphaned : " << conflict.orphaned << "\n";
137 oss << " left_rid : " << conflict.left_rid << "\n";
138 oss << " right_rid : " << conflict.right_rid << "\n";
139 oss << " left_resolution : " << image(conflict.left_resolution);
140 oss << " right_resolution: " << image(conflict.right_resolution);
141 out = oss.str();
142}
143
144template <> void
145dump(duplicate_name_conflict const & conflict, string & out)
146{
147 ostringstream oss;
148 oss << "duplicate_name_conflict between left node: " << conflict.left_nid << " "
149 << "and right node: " << conflict.right_nid << " "
150 << "parent: " << conflict.parent_name.first << " "
151 << "basename: " << conflict.parent_name.second << " "
152 << "left_resolution: " << image(conflict.left_resolution)
153 << "right_resolution: " << image(conflict.right_resolution)
154 << "\n";
155 out = oss.str();
156}
157
158template <> void
159dump(attribute_conflict const & conflict, string & out)
160{
161 ostringstream oss;
162 oss << "attribute_conflict on node: " << conflict.nid << " "
163 << "attr: '" << conflict.key << "' "
164 << "left: " << conflict.left.first << " '" << conflict.left.second << "' "
165 << "right: " << conflict.right.first << " '" << conflict.right.second << "'\n";
166 out = oss.str();
167}
168
169template <> void
170dump(file_content_conflict const & conflict, string & out)
171{
172 ostringstream oss;
173 oss << "file_content_conflict on node: " << conflict.nid;
174 oss << " resolution: " << image(conflict.resolution);
175 oss << "\n";
176 out = oss.str();
177}
178
179void
180roster_merge_result::clear()
181{
182 missing_root_conflict = false;
183 invalid_name_conflicts.clear();
184 directory_loop_conflicts.clear();
185
186 orphaned_node_conflicts.clear();
187 multiple_name_conflicts.clear();
188 dropped_modified_conflicts.clear();
189 duplicate_name_conflicts.clear();
190
191 attribute_conflicts.clear();
192 file_content_conflicts.clear();
193
194 roster = roster_t();
195}
196
197bool
198roster_merge_result::is_clean() const
199{
200 return !has_non_content_conflicts()
201 && !has_content_conflicts();
202}
203
204bool
205roster_merge_result::has_content_conflicts() const
206{
207 return !file_content_conflicts.empty();
208}
209
210bool
211roster_merge_result::has_non_content_conflicts() const
212{
213 return missing_root_conflict
214 || !invalid_name_conflicts.empty()
215 || !directory_loop_conflicts.empty()
216 || !orphaned_node_conflicts.empty()
217 || !multiple_name_conflicts.empty()
218 || !dropped_modified_conflicts.empty()
219 || !duplicate_name_conflicts.empty()
220 || !attribute_conflicts.empty();
221}
222
223int
224roster_merge_result::count_supported_resolution() const
225{
226 return orphaned_node_conflicts.size()
227 + dropped_modified_conflicts.size()
228 + file_content_conflicts.size()
229 + duplicate_name_conflicts.size();
230}
231
232int
233roster_merge_result::count_unsupported_resolution() const
234{
235 return (missing_root_conflict ? 1 : 0)
236 + invalid_name_conflicts.size()
237 + directory_loop_conflicts.size()
238 + multiple_name_conflicts.size()
239 + attribute_conflicts.size();
240}
241
242static void
243dump_conflicts(roster_merge_result const & result, string & out)
244{
245 if (result.missing_root_conflict)
246 out += (FL("missing_root_conflict: root directory has been removed\n"))
247 .str();
248
249 dump(result.invalid_name_conflicts, out);
250 dump(result.directory_loop_conflicts, out);
251
252 dump(result.orphaned_node_conflicts, out);
253 dump(result.multiple_name_conflicts, out);
254 dump(result.dropped_modified_conflicts, out);
255 dump(result.duplicate_name_conflicts, out);
256
257 dump(result.attribute_conflicts, out);
258 dump(result.file_content_conflicts, out);
259}
260
261template <> void
262dump(roster_merge_result const & result, string & out)
263{
264 dump_conflicts(result, out);
265
266 string roster_part;
267 dump(result.roster, roster_part);
268 out += "\n\n";
269 out += roster_part;
270}
271
272void
273roster_merge_result::log_conflicts() const
274{
275 string str;
276 dump_conflicts(*this, str);
277 L(FL("%s") % str);
278}
279
280namespace
281{
282 // a wins if *(b) > a. Which is to say that all members of b_marks are
283 // ancestors of a. But all members of b_marks are ancestors of the
284 // _b_, so the previous statement is the same as saying that _no_
285 // members of b_marks is an _uncommon_ ancestor of _b_.
286 bool
287 a_wins(set<revision_id> const & b_marks,
288 set<revision_id> const & b_uncommon_ancestors)
289 {
290 for (set<revision_id>::const_iterator i = b_marks.begin();
291 i != b_marks.end(); ++i)
292 if (b_uncommon_ancestors.find(*i) != b_uncommon_ancestors.end())
293 return false;
294 return true;
295 }
296
297 // returns true if merge was successful ('result' is valid), false otherwise
298 // ('conflict_descriptor' is valid).
299 template <typename T, typename C> bool
300 merge_scalar(T const & left,
301 set<revision_id> const & left_marks,
302 set<revision_id> const & left_uncommon_ancestors,
303 T const & right,
304 set<revision_id> const & right_marks,
305 set<revision_id> const & right_uncommon_ancestors,
306 T & result,
307 C & conflict_descriptor)
308 {
309 if (left == right)
310 {
311 result = left;
312 return true;
313 }
314 MM(left_marks);
315 MM(left_uncommon_ancestors);
316 MM(right_marks);
317 MM(right_uncommon_ancestors);
318 bool left_wins = a_wins(right_marks, right_uncommon_ancestors);
319 bool right_wins = a_wins(left_marks, left_uncommon_ancestors);
320 // two bools means 4 cases:
321
322 // this is ambiguous clean merge, which is theoretically impossible.
323 I(!(left_wins && right_wins));
324
325 if (left_wins && !right_wins)
326 {
327 result = left;
328 return true;
329 }
330
331 if (!left_wins && right_wins)
332 {
333 result = right;
334 return true;
335 }
336
337 if (!left_wins && !right_wins)
338 {
339 conflict_descriptor.left = left;
340 conflict_descriptor.right = right;
341 return false;
342 }
343 I(false);
344 }
345
346 inline void
347 create_node_for(node_t const & n, roster_t & new_roster)
348 {
349 if (is_dir_t(n))
350 new_roster.create_dir_node(n->self);
351 else if (is_file_t(n))
352 new_roster.create_file_node(file_id(), n->self);
353 else
354 I(false);
355 }
356
357 inline void
358 insert_if_unborn(node_t const & n,
359 marking_map const & markings,
360 set<revision_id> const & uncommon_ancestors,
361 roster_t const & parent_roster,
362 side_t const present_in,
363 roster_merge_result & result)
364 {
365 const_marking_t const & m = markings.get_marking(n->self);
366 revision_id const & birth = m->birth_revision;
367 if (uncommon_ancestors.find(birth) != uncommon_ancestors.end())
368 create_node_for(n, result.roster);
369 else
370 {
371 // The node has been deleted from the other side of the merge. If
372 // there are changes to the file on this side of the merge, insert
373 // it into the new roster, but leave it detached, so the conflict
374 // resolutions can deal with it easily. Note that attaching would be
375 // done later; see roster_merge below.
376 //
377 // We also need to look for another node with the same name; user
378 // may have already 'undeleted' the node. But we have to do that
379 // after all result nodes are created and attached.
380 set<revision_id> const & content_marks = m->file_content;
381 for (set<revision_id>::const_iterator it = content_marks.begin();
382 it != content_marks.end();
383 it++)
384 {
385 if (uncommon_ancestors.find(*it) != uncommon_ancestors.end())
386 {
387 dropped_modified_conflict conflict;
388 attr_key a_key = typecast_vocab<attr_key>(utf8("mtn:resolve_conflict"));
389 attr_map_t::const_iterator i = n->attrs.find(a_key);
390
391 create_node_for(n, result.roster);
392
393 switch (present_in)
394 {
395 case left_side:
396 conflict = dropped_modified_conflict(n->self, the_null_node);
397 break;
398 case right_side:
399 conflict = dropped_modified_conflict(the_null_node, n->self);
400 break;
401 }
402
403 if (i != n->attrs.end() && i->second.first)
404 {
405 if (i->second.second == typecast_vocab<attr_value>(utf8("drop")))
406 {
407 switch (present_in)
408 {
409 case left_side:
410 conflict.left_resolution.resolution = resolve_conflicts::drop;
411 break;
412 case right_side:
413 conflict.right_resolution.resolution = resolve_conflicts::drop;
414 break;
415 }
416 }
417 else
418 {
419 E(false, origin::user,
420 F("unsupported '%s' conflict resolution in mtn:resolve_conflict attribute") %
421 i->second.first);
422 }
423 }
424
425 result.dropped_modified_conflicts.push_back(conflict);
426 return;
427 }
428 }
429 }
430 }
431
432 bool
433 would_make_dir_loop(roster_t const & r, node_id nid, node_id parent)
434 {
435 // parent may not be fully attached yet; that's okay. that just means
436 // we'll run into a node with a null parent somewhere before we hit the
437 // actual root; whether we hit the actual root or not, hitting a node
438 // with a null parent will tell us that this particular attachment won't
439 // create a loop.
440 for (node_id curr = parent; !null_node(curr); curr = r.get_node(curr)->parent)
441 {
442 if (curr == nid)
443 return true;
444 }
445 return false;
446 }
447
448 void
449 assign_name(roster_merge_result & result, node_id nid,
450 node_id parent, path_component name, side_t side)
451 {
452 // this function is reponsible for detecting structural conflicts. by the
453 // time we've gotten here, we have a node that's unambiguously decided on
454 // a name; but it might be that that name does not exist (because the
455 // parent dir is gone), or that it's already taken (by another node), or
456 // that putting this node there would create a directory loop. In all
457 // such cases, rather than actually attach the node, we write a conflict
458 // structure and leave it detached.
459
460 // the root dir is somewhat special. it can't be orphaned, and it can't
461 // make a dir loop. it can, however, have a name collision.
462 if (null_node(parent))
463 {
464 I(name.empty());
465 if (result.roster.has_root())
466 {
467 // see comments below about name collisions.
468 duplicate_name_conflict c;
469 // some other node has already been attached at the root location
470 // so write a conflict structure with this node on the indicated
471 // side of the merge and the attached node on the other side of
472 // the merge. detach the previously attached node and leave both
473 // conflicted nodes detached.
474 switch (side)
475 {
476 case left_side:
477 c.left_nid = nid;
478 c.right_nid = result.roster.root()->self;
479 break;
480 case right_side:
481 c.left_nid = result.roster.root()->self;
482 c.right_nid = nid;
483 break;
484 }
485 c.parent_name = make_pair(parent, name);
486 result.roster.detach_node(file_path());
487 result.duplicate_name_conflicts.push_back(c);
488 return;
489 }
490 }
491 else
492 {
493 // We need this in two places
494 std::vector<dropped_modified_conflict>::iterator dropped_modified =
495 find(result.dropped_modified_conflicts.begin(),
496 result.dropped_modified_conflicts.end(),
497 nid);
498
499 // orphan:
500 if (!result.roster.has_node(parent))
501 {
502 // If the orphaned node is due to the parent directory being
503 // dropped, and the orphaned node is modified, then it already
504 // has a dropped_modified conflict; add the orphaned information
505 // to that.
506
507 if (result.dropped_modified_conflicts.end() != dropped_modified)
508 {
509 dropped_modified->orphaned = true;
510 return;
511 }
512 else
513 {
514 orphaned_node_conflict c;
515 c.nid = nid;
516 c.parent_name = make_pair(parent, name);
517 result.orphaned_node_conflicts.push_back(c);
518 return;
519 }
520 }
521
522 dir_t p = downcast_to_dir_t(result.roster.get_node_for_update(parent));
523
524 // duplicate name conflict:
525 // see the comment in roster_merge.hh for the analysis showing that at
526 // most two nodes can participate in a duplicate name conflict. this code
527 // exploits that; after this code runs, there will be no node at the given
528 // location in the tree, which means that in principle, if there were a
529 // third node that _also_ wanted to go here, when we got around to
530 // attaching it we'd have no way to realize it should be a conflict. but
531 // that never happens, so we don't have to keep a lookaside set of
532 // "poisoned locations" or anything.
533 if (p->has_child(name))
534 {
535 duplicate_name_conflict c;
536 // some other node has already been attached at the named location
537 // so write a conflict structure with this node on the indicated
538 // side of the merge and the attached node on the other side of
539 // the merge. detach the previously attached node and leave both
540 // conflicted nodes detached.
541 switch (side)
542 {
543 case left_side:
544 c.left_nid = nid;
545 c.right_nid = p->get_child(name)->self;
546 break;
547 case right_side:
548 c.left_nid = p->get_child(name)->self;
549 c.right_nid = nid;
550 break;
551 }
552 c.parent_name = make_pair(parent, name);
553 p->detach_child(name);
554 result.duplicate_name_conflicts.push_back(c);
555 return;
556 }
557
558 if (would_make_dir_loop(result.roster, nid, parent))
559 {
560 directory_loop_conflict c;
561 c.nid = nid;
562 c.parent_name = make_pair(parent, name);
563 result.directory_loop_conflicts.push_back(c);
564 return;
565 }
566
567 if (result.dropped_modified_conflicts.end() != dropped_modified)
568 {
569 // conflict already entered, just don't attach
570 return;
571 }
572 }
573 // hey, we actually made it. attach the node!
574 result.roster.attach_node(nid, parent, name);
575 }
576
577 void
578 copy_node_forward(roster_merge_result & result, node_t const & n,
579 node_t const & old_n, side_t const & side)
580 {
581 I(n->self == old_n->self);
582 n->attrs = old_n->attrs;
583 if (is_file_t(n))
584 downcast_to_file_t(n)->content = downcast_to_file_t(old_n)->content;
585 assign_name(result, n->self, old_n->parent, old_n->name, side);
586 }
587
588} // end anonymous namespace
589
590void
591roster_merge(roster_t const & left_parent,
592 marking_map const & left_markings,
593 set<revision_id> const & left_uncommon_ancestors,
594 roster_t const & right_parent,
595 marking_map const & right_markings,
596 set<revision_id> const & right_uncommon_ancestors,
597 roster_merge_result & result)
598{
599 L(FL("Performing a roster_merge"));
600
601 result.clear();
602 MM(left_parent);
603 MM(left_markings);
604 MM(right_parent);
605 MM(right_markings);
606 MM(result);
607
608 // First handle lifecycles, by die-die-die merge -- our result will contain
609 // everything that is alive in both parents, or alive in one and unborn in
610 // the other, exactly.
611 {
612 parallel::iter<node_map> i(left_parent.all_nodes(), right_parent.all_nodes());
613 while (i.next())
614 {
615 switch (i.state())
616 {
617 case parallel::invalid:
618 I(false);
619
620 case parallel::in_left:
621 insert_if_unborn(i.left_data(),
622 left_markings, left_uncommon_ancestors, left_parent,
623 left_side, // present_in
624 result);
625 break;
626
627 case parallel::in_right:
628 insert_if_unborn(i.right_data(),
629 right_markings, right_uncommon_ancestors, right_parent,
630 right_side, // present_in
631 result);
632 break;
633
634 case parallel::in_both:
635 create_node_for(i.left_data(), result.roster);
636 break;
637 }
638 }
639 }
640
641 // okay, our roster now contains a bunch of empty, detached nodes. fill
642 // them in one at a time with *-merge.
643 {
644 node_map::const_iterator left_i, right_i;
645 parallel::iter<node_map> i(left_parent.all_nodes(), right_parent.all_nodes());
646 node_map::const_iterator new_i = result.roster.all_nodes().begin();
647 marking_map::const_iterator left_mi = left_markings.begin();
648 marking_map::const_iterator right_mi = right_markings.begin();
649 while (i.next())
650 {
651 switch (i.state())
652 {
653 case parallel::invalid:
654 I(false);
655
656 case parallel::in_left:
657 {
658 node_t const & left_n = i.left_data();
659 // we skip nodes that aren't in the result roster (were
660 // deleted in the lifecycles step above)
661 if (result.roster.has_node(left_n->self))
662 {
663 // attach this node from the left roster. this may cause
664 // a name collision with the previously attached node from
665 // the other side of the merge.
666 copy_node_forward(result, new_i->second, left_n, left_side);
667 ++new_i;
668 }
669 ++left_mi;
670 break;
671 }
672
673 case parallel::in_right:
674 {
675 node_t const & right_n = i.right_data();
676 // we skip nodes that aren't in the result roster
677 if (result.roster.has_node(right_n->self))
678 {
679 // attach this node from the right roster. this may cause
680 // a name collision with the previously attached node from
681 // the other side of the merge.
682 copy_node_forward(result, new_i->second, right_n, right_side);
683 ++new_i;
684 }
685 ++right_mi;
686 break;
687 }
688
689 case parallel::in_both:
690 {
691 I(new_i->first == i.left_key());
692 I(left_mi->first == i.left_key());
693 I(right_mi->first == i.right_key());
694 node_t const & left_n = i.left_data();
695 marking_t const & left_marking = left_mi->second;
696 node_t const & right_n = i.right_data();
697 marking_t const & right_marking = right_mi->second;
698 node_t const & new_n = new_i->second;
699 // merge name
700 {
701 pair<node_id, path_component> left_name, right_name, new_name;
702 multiple_name_conflict conflict(new_n->self);
703 left_name = make_pair(left_n->parent, left_n->name);
704 right_name = make_pair(right_n->parent, right_n->name);
705 if (merge_scalar(left_name,
706 left_marking->parent_name,
707 left_uncommon_ancestors,
708 right_name,
709 right_marking->parent_name,
710 right_uncommon_ancestors,
711 new_name, conflict))
712 {
713 side_t winning_side;
714
715 if (new_name == left_name)
716 winning_side = left_side;
717 else if (new_name == right_name)
718 winning_side = right_side;
719 else
720 I(false);
721
722 // attach this node from the winning side of the merge. if
723 // there is a name collision the previously attached node
724 // (which is blocking this one) must come from the other
725 // side of the merge.
726 assign_name(result, new_n->self,
727 new_name.first, new_name.second, winning_side);
728
729 }
730 else
731 {
732 // unsuccessful merge; leave node detached and save
733 // conflict object
734 result.multiple_name_conflicts.push_back(conflict);
735 }
736 }
737 // if a file, merge content
738 if (is_file_t(new_n))
739 {
740 file_content_conflict conflict(new_n->self);
741 if (merge_scalar(downcast_to_file_t(left_n)->content,
742 left_marking->file_content,
743 left_uncommon_ancestors,
744 downcast_to_file_t(right_n)->content,
745 right_marking->file_content,
746 right_uncommon_ancestors,
747 downcast_to_file_t(new_n)->content,
748 conflict))
749 {
750 // successful merge
751 }
752 else
753 {
754 downcast_to_file_t(new_n)->content = file_id();
755 result.file_content_conflicts.push_back(conflict);
756 }
757 }
758 // merge attributes
759 {
760 attr_map_t::const_iterator left_ai = left_n->attrs.begin();
761 attr_map_t::const_iterator right_ai = right_n->attrs.begin();
762 parallel::iter<attr_map_t> attr_i(left_n->attrs,
763 right_n->attrs);
764 while(attr_i.next())
765 {
766 switch (attr_i.state())
767 {
768 case parallel::invalid:
769 I(false);
770 case parallel::in_left:
771 safe_insert(new_n->attrs, attr_i.left_value());
772 break;
773 case parallel::in_right:
774 safe_insert(new_n->attrs, attr_i.right_value());
775 break;
776 case parallel::in_both:
777 pair<bool, attr_value> new_value;
778 attribute_conflict conflict(new_n->self);
779 conflict.key = attr_i.left_key();
780 I(conflict.key == attr_i.right_key());
781 if (merge_scalar(attr_i.left_data(),
782 safe_get(left_marking->attrs,
783 attr_i.left_key()),
784 left_uncommon_ancestors,
785 attr_i.right_data(),
786 safe_get(right_marking->attrs,
787 attr_i.right_key()),
788 right_uncommon_ancestors,
789 new_value,
790 conflict))
791 {
792 // successful merge
793 safe_insert(new_n->attrs,
794 make_pair(attr_i.left_key(),
795 new_value));
796 }
797 else
798 {
799 // unsuccessful merge
800 // leave out the attr entry entirely, and save the
801 // conflict
802 result.attribute_conflicts.push_back(conflict);
803 }
804 break;
805 }
806
807 }
808 }
809 }
810 ++left_mi;
811 ++right_mi;
812 ++new_i;
813 break;
814 }
815 }
816 I(left_mi == left_markings.end());
817 I(right_mi == right_markings.end());
818 I(new_i == result.roster.all_nodes().end());
819 }
820
821 // now we can look for dropped_modified conflicts with recreated nodes or
822 // duplicate names
823 for (size_t i = 0; i < result.dropped_modified_conflicts.size(); ++i)
824 {
825 dropped_modified_conflict & conflict = result.dropped_modified_conflicts[i];
826
827 // If the file name was recreated, it is present in the result with
828 // the modified_name but different node id; find that and unattach it.
829 // Or, it may now subject to a duplicate_name conflict (see
830 // test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2).
831 // In which case modified_name will be present in the other parent,
832 // but not in the result.
833
834 file_path modified_name;
835 node_id nid;
836 bool duplicate_name = false;
837
838 switch (conflict.dropped_side)
839 {
840 case resolve_conflicts::left_side:
841 right_parent.get_name(conflict.right_nid, modified_name);
842
843 if (result.roster.has_node(modified_name))
844 {
845 // recreated; we need to detach the node for the conflict
846 // resolution process. We'd like to just do:
847 // result.roster.detach_node(modified_name);
848 // but that doesn't erase result.roster.old_locations properly
849
850 const_node_t const & left_node = left_parent.get_node(modified_name);
851 node_id parent = left_node->parent;
852 path_component component_name = left_node->name;
853 dir_t p = downcast_to_dir_t(result.roster.get_node_for_update(parent));
854 conflict.left_nid = left_node->self;
855 p->detach_child(component_name);
856 }
857 else if (left_parent.has_node (modified_name))
858 {
859 conflict.left_nid = left_parent.get_node(modified_name)->self;
860 nid = conflict.left_nid;
861 duplicate_name = true;
862 }
863 break;
864
865 case resolve_conflicts::right_side:
866 left_parent.get_name(conflict.left_nid, modified_name);
867
868 if (result.roster.has_node(modified_name))
869 {
870 // recreated; see comment in left_side above
871 const_node_t const & right_node = right_parent.get_node(modified_name);
872 node_id parent = right_node->parent;
873 path_component component_name = right_node->name;
874 dir_t p = downcast_to_dir_t(result.roster.get_node_for_update(parent));
875 conflict.right_nid = right_node->self;
876 p->detach_child(component_name);
877 }
878 else if (right_parent.has_node (modified_name))
879 {
880 conflict.right_nid = right_parent.get_node(modified_name)->self;
881 nid = conflict.right_nid;
882 duplicate_name = true;
883 }
884 break;
885 }
886
887 if (duplicate_name)
888 {
889 // delete the duplicate name conflict; it will be handled by dropped_modified.
890 std::vector<duplicate_name_conflict>::iterator i =
891 find(result.duplicate_name_conflicts.begin(),
892 result.duplicate_name_conflicts.end(),
893 nid);
894
895 result.duplicate_name_conflicts.erase(i);
896 }
897 } // end dropped_modified loop
898
899 // now check for the possible global problems
900 if (!result.roster.has_root())
901 result.missing_root_conflict = true;
902 else
903 {
904 // we can't have an illegal _MTN dir unless we have a root node in the
905 // first place...
906 dir_t result_root = result.roster.root();
907
908 if (result_root->has_child(bookkeeping_root_component))
909 {
910 invalid_name_conflict conflict;
911 node_t n = result_root->get_child(bookkeeping_root_component);
912 conflict.nid = n->self;
913 conflict.parent_name.first = n->parent;
914 conflict.parent_name.second = n->name;
915 I(n->name == bookkeeping_root_component);
916
917 result.roster.detach_node(n->self);
918 result.invalid_name_conflicts.push_back(conflict);
919 }
920 }
921}
922
923
924// Local Variables:
925// mode: C++
926// fill-column: 76
927// c-file-style: "gnu"
928// indent-tabs-mode: nil
929// End:
930// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status