# # # patch "cmd_merging.cc" # from [e13d083c6ec5a0bb84b72a793b3fefccf5fe980f] # to [ff3d1325c7fdb66023507ee8fd41cc5858495e4b] # # patch "diff_patch.cc" # from [fd2359b6d78c7fa64d5c380f3ca6c72fe53d68f7] # to [cdb4b1b3e7008e0e7b0f6e3f338331fe20525ece] # # patch "merge.cc" # from [76da4cf756a8b84f177c0540bf1cf66286662878] # to [ad37299ca3a569d95a5591f0ecbf4cbca22d8d96] # # patch "roster_merge.cc" # from [3cae80e9fd925941eda7b626de9cc8542a44f628] # to [17f4b5ac24ef49e234af24f7cde4d9223165d433] # # patch "roster_merge.hh" # from [9110ec12f5d3d21a193067a1937b8d7c2d750546] # to [4e12424804e19a3ad5c353d6bbb7b0c779be4519] # # patch "tests/conflict_messages/__driver__.lua" # from [32d64b7bea2cf1e7c1c98ad6a1579762a4f6e5b1] # to [c2eecfd2d5bb9786bf10e0710a3e87d9c94f898e] # ============================================================ --- cmd_merging.cc e13d083c6ec5a0bb84b72a793b3fefccf5fe980f +++ cmd_merging.cc ff3d1325c7fdb66023507ee8fd41cc5858495e4b @@ -820,14 +820,16 @@ CMD(show_conflicts, "show_conflicts", "" content_merge_database_adaptor adaptor(app, l_id, r_id, l_marking, r_marking); - result.report_multiple_name_conflicts(l_roster, r_roster, adaptor); - result.report_file_content_conflicts(l_roster, r_roster, adaptor); - result.report_attribute_conflicts(l_roster, r_roster, adaptor); + result.report_missing_root_conflicts(l_roster, r_roster, adaptor); + result.report_invalid_name_conflicts(l_roster, r_roster, adaptor); + result.report_directory_loop_conflicts(l_roster, r_roster, adaptor); + result.report_orphaned_node_conflicts(l_roster, r_roster, adaptor); + result.report_multiple_name_conflicts(l_roster, r_roster, adaptor); result.report_duplicate_name_conflicts(l_roster, r_roster, adaptor); - result.report_directory_loop_conflicts(l_roster, r_roster, adaptor); - result.report_invalid_name_conflicts(l_roster, r_roster, adaptor); - result.report_missing_root_conflicts(l_roster, r_roster, adaptor); + + result.report_attribute_conflicts(l_roster, r_roster, adaptor); + result.report_file_content_conflicts(l_roster, r_roster, adaptor); } } ============================================================ --- diff_patch.cc fd2359b6d78c7fa64d5c380f3ca6c72fe53d68f7 +++ diff_patch.cc cdb4b1b3e7008e0e7b0f6e3f338331fe20525ece @@ -764,6 +764,10 @@ content_merger::attribute_manual_merge(f return false; // default: enable auto merge } +// TODO: split this into auto_merge_files and manual_merge_files +// then make an automatic merge pass over all conflicting content hashes +// and list all remaining conflicts before asking the user to merge files + bool content_merger::try_to_merge_files(file_path const & anc_path, file_path const & left_path, ============================================================ --- merge.cc 76da4cf756a8b84f177c0540bf1cf66286662878 +++ merge.cc ad37299ca3a569d95a5591f0ecbf4cbca22d8d96 @@ -56,14 +56,16 @@ resolve_merge_conflicts(roster_t const & if (result.has_non_content_conflicts()) { - result.report_multiple_name_conflicts(left_roster, right_roster, adaptor); - result.report_file_content_conflicts(left_roster, right_roster, adaptor); - result.report_attribute_conflicts(left_roster, right_roster, adaptor); + result.report_missing_root_conflicts(left_roster, right_roster, adaptor); + result.report_invalid_name_conflicts(left_roster, right_roster, adaptor); + result.report_directory_loop_conflicts(left_roster, right_roster, adaptor); + result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor); + result.report_multiple_name_conflicts(left_roster, right_roster, adaptor); result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor); - result.report_directory_loop_conflicts(left_roster, right_roster, adaptor); - result.report_invalid_name_conflicts(left_roster, right_roster, adaptor); - result.report_missing_root_conflicts(left_roster, right_roster, adaptor); + + result.report_attribute_conflicts(left_roster, right_roster, adaptor); + result.report_file_content_conflicts(left_roster, right_roster, adaptor); } else if (result.has_content_conflicts()) { @@ -71,8 +73,17 @@ resolve_merge_conflicts(roster_t const & // To do this requires finding a merge ancestor. L(FL("examining content conflicts")); + + // FIXME: need this for the tests to pass but it doesn't really make sense + // because the auto merger may resolve all of the content conflicts + result.report_file_content_conflicts(left_roster, right_roster, adaptor); + // TODO: split this into two merge passes + // first make an automatic merge pass over all content conflicts + // then list all remaining conflicts before making a second + // manual merge pass over the remaining conflicts + size_t cnt; size_t total_conflicts = result.file_content_conflicts.size(); std::vector::iterator it; ============================================================ --- roster_merge.cc 3cae80e9fd925941eda7b626de9cc8542a44f628 +++ roster_merge.cc 17f4b5ac24ef49e234af24f7cde4d9223165d433 @@ -26,45 +26,44 @@ template <> void using std::string; template <> void -dump(multiple_name_conflict const & conflict, string & out) +dump(invalid_name_conflict const & conflict, string & out) { ostringstream oss; - oss << "multiple_name_conflict on node: " << conflict.nid << " " - << "left parent: " << conflict.left.first << " " - << "basename: " << conflict.left.second << " " - << "right parent: " << conflict.right.first << " " - << "basename: " << conflict.right.second << "\n"; + oss << "invalid_name_conflict on node: " << conflict.nid << " " + << "parent: " << conflict.parent_name.first << " " + << "basename: " << conflict.parent_name.second << "\n"; out = oss.str(); } template <> void -dump(file_content_conflict const & conflict, string & out) +dump(directory_loop_conflict const & conflict, string & out) { ostringstream oss; - oss << "file_content_conflict on node: " << conflict.nid << " " - << "left: " << conflict.left << " " - << "right: " << conflict.right << "\n"; + oss << "directory_loop_conflict on node: " << conflict.nid << " " + << "parent: " << conflict.parent_name.first << " " + << "basename: " << conflict.parent_name.second << "\n"; out = oss.str(); } template <> void -dump(attribute_conflict const & conflict, string & out) +dump(orphaned_node_conflict const & conflict, string & out) { ostringstream oss; - oss << "attribute_conflict on node: " << conflict.nid << " " - << "attr: '" << conflict.key << "' " - << "left: " << conflict.left.first << " '" << conflict.left.second << "' " - << "right: " << conflict.right.first << " '" << conflict.right.second << "'\n"; + oss << "orphaned_node_conflict on node: " << conflict.nid << " " + << "parent: " << conflict.parent_name.first << " " + << "basename: " << conflict.parent_name.second << "\n"; out = oss.str(); } template <> void -dump(orphaned_node_conflict const & conflict, string & out) +dump(multiple_name_conflict const & conflict, string & out) { ostringstream oss; - oss << "orphaned_node_conflict on node: " << conflict.nid << " " - << "parent: " << conflict.parent_name.first << " " - << "basename: " << conflict.parent_name.second << "\n"; + oss << "multiple_name_conflict on node: " << conflict.nid << " " + << "left parent: " << conflict.left.first << " " + << "basename: " << conflict.left.second << " " + << "right parent: " << conflict.right.first << " " + << "basename: " << conflict.right.second << "\n"; out = oss.str(); } @@ -80,22 +79,23 @@ template <> void } template <> void -dump(directory_loop_conflict const & conflict, string & out) +dump(attribute_conflict const & conflict, string & out) { ostringstream oss; - oss << "directory_loop_conflict on node: " << conflict.nid << " " - << "parent: " << conflict.parent_name.first << " " - << "basename: " << conflict.parent_name.second << "\n"; + oss << "attribute_conflict on node: " << conflict.nid << " " + << "attr: '" << conflict.key << "' " + << "left: " << conflict.left.first << " '" << conflict.left.second << "' " + << "right: " << conflict.right.first << " '" << conflict.right.second << "'\n"; out = oss.str(); } template <> void -dump(invalid_name_conflict const & conflict, string & out) +dump(file_content_conflict const & conflict, string & out) { ostringstream oss; - oss << "invalid_name_conflict on node: " << conflict.nid << " " - << "parent: " << conflict.parent_name.first << " " - << "basename: " << conflict.parent_name.second << "\n"; + oss << "file_content_conflict on node: " << conflict.nid << " " + << "left: " << conflict.left << " " + << "right: " << conflict.right << "\n"; out = oss.str(); } @@ -106,14 +106,6 @@ roster_merge_result::is_clean() const && !has_content_conflicts(); } -// possibly split this into -// -// has_structure_conflicts -// has_attribute_conflicts -// has_content_conflicts -// -// and resolve them in that order - bool roster_merge_result::has_content_conflicts() const { @@ -123,27 +115,29 @@ roster_merge_result::has_non_content_con bool roster_merge_result::has_non_content_conflicts() const { - return !multiple_name_conflicts.empty() - || !attribute_conflicts.empty() + return missing_root_dir + || !invalid_name_conflicts.empty() + || !directory_loop_conflicts.empty() || !orphaned_node_conflicts.empty() + || !multiple_name_conflicts.empty() || !duplicate_name_conflicts.empty() - || !directory_loop_conflicts.empty() - || !invalid_name_conflicts.empty() - || missing_root_dir; + || !attribute_conflicts.empty(); } static void dump_conflicts(roster_merge_result const & result, string & out) { - dump(result.multiple_name_conflicts, out); - dump(result.file_content_conflicts, out); - dump(result.attribute_conflicts, out); + if (result.missing_root_dir) + out += (FL("missing root conflict: root directory has been removed")).str(); + + dump(result.invalid_name_conflicts, out); + dump(result.directory_loop_conflicts, out); + dump(result.orphaned_node_conflicts, out); + dump(result.multiple_name_conflicts, out); dump(result.duplicate_name_conflicts, out); - dump(result.directory_loop_conflicts, out); - dump(result.invalid_name_conflicts, out); - if (result.missing_root_dir) - out += (FL("missing root conflict: root directory has been removed")).str(); + dump(result.attribute_conflicts, out); + dump(result.file_content_conflicts, out); } template <> void @@ -185,158 +179,190 @@ void } void -roster_merge_result::report_multiple_name_conflicts(roster_t const & left_roster, - roster_t const & right_roster, - content_merge_adaptor & adaptor) const +roster_merge_result::report_missing_root_conflicts(roster_t const & left_roster, + roster_t const & right_roster, + content_merge_adaptor & adaptor) const { - // TODO: - // - W on each conflict type and then P further details - // - add a better error macro (D?) for these multiple problem cases - // - need a way to pring blank lines, with no "mtn:" prefix - MM(left_roster); MM(right_roster); - for (size_t i = 0; i < multiple_name_conflicts.size(); ++i) + if (missing_root_dir) { - multiple_name_conflict const & conflict = multiple_name_conflicts[i]; - MM(conflict); + node_id left_root, right_root; + left_root = left_roster.root()->self; + right_root = right_roster.root()->self; - I(!roster.is_attached(conflict.nid)); + P(F("conflict: missing root directory")); - file_path left_name, right_name; + if (left_roster.has_node(right_root) && + !right_roster.has_node(left_root)) + { + shared_ptr lca_roster; + revision_id lca_rid; + file_path lca_name; - left_roster.get_name(conflict.nid, left_name); - right_roster.get_name(conflict.nid, right_name); + adaptor.get_ancestral_roster(left_root, lca_rid, lca_roster); + lca_roster->get_name(left_root, lca_name); - shared_ptr lca_roster; - revision_id lca_rid; - file_path lca_name; + P(F("directory '%s' pivoted to root on the left") % lca_name); + P(F("directory '%s' deleted on the right") % lca_name); + } + else if (!left_roster.has_node(right_root) && + right_roster.has_node(left_root)) + { + shared_ptr lca_roster; + revision_id lca_rid; + file_path lca_name; - adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); - lca_roster->get_name(conflict.nid, lca_name); + adaptor.get_ancestral_roster(right_root, lca_rid, lca_roster); + lca_roster->get_name(right_root, lca_name); - P(F("conflict: multiple names for %s '%s' from revision %s") - % get_type(*lca_roster, conflict.nid) % lca_name % lca_rid); - P(F("renamed to '%s' on the left") % left_name); - P(F("renamed to '%s' on the right") % right_name); + P(F("directory '%s' pivoted to root on the right") % lca_name); + P(F("directory '%s' deleted on the left") % lca_name); + } + else if (!left_roster.has_node(right_root) && + !right_roster.has_node(left_root)) + { + shared_ptr left_lca_roster, right_lca_roster; + revision_id left_lca_rid, right_lca_rid; + file_path left_lca_name, right_lca_name; + + adaptor.get_ancestral_roster(left_root, left_lca_rid, + left_lca_roster); + adaptor.get_ancestral_roster(right_root, right_lca_rid, + right_lca_roster); + + left_lca_roster->get_name(left_root, left_lca_name); + right_lca_roster->get_name(right_root, right_lca_name); + + P(F("directory '%s' pivoted to root on the left") % left_lca_name); + P(F("directory '%s' deleted on the right") % left_lca_name); + + P(F("directory '%s' deleted on the left") % right_lca_name); + P(F("directory '%s' pivoted to root on the right") % right_lca_name); + } + else + I(false); } } void -roster_merge_result::report_file_content_conflicts(roster_t const & left_roster, +roster_merge_result::report_invalid_name_conflicts(roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor) const { MM(left_roster); MM(right_roster); - for (size_t i = 0; i < file_content_conflicts.size(); ++i) + for (size_t i = 0; i < invalid_name_conflicts.size(); ++i) { - file_content_conflict const & conflict = file_content_conflicts[i]; + invalid_name_conflict const & conflict = invalid_name_conflicts[i]; MM(conflict); - if (roster.is_attached(conflict.nid)) + I(!roster.is_attached(conflict.nid)); + + shared_ptr lca_roster, parent_lca_roster; + revision_id lca_rid, parent_lca_rid; + file_path lca_name, lca_parent_name; + + adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); + lca_roster->get_name(conflict.nid, lca_name); + lca_roster->get_name(conflict.parent_name.first, lca_parent_name); + + adaptor.get_ancestral_roster(conflict.parent_name.first, + parent_lca_rid, parent_lca_roster); + + P(F("conflict: invalid name _MTN in root directory")); + + if (left_roster.root()->self == conflict.parent_name.first) { - file_path name; - roster.get_name(conflict.nid, name); + P(F("'%s' pivoted to root on the left") + % lca_parent_name); - P(F("conflict: content conflict on file '%s'") - % name); - P(F("content hash is %s on the left") % conflict.left); - P(F("content hash is %s on the right") % conflict.right); + file_path right_name; + right_roster.get_name(conflict.nid, right_name); + if (parent_lca_roster->has_node(conflict.nid)) + { + P(F("'%s' renamed to '%s' on the right") + % lca_name % right_name); + } + else + { + P(F("'%s' added in revision %s on the right") + % right_name % lca_rid); + } } - else + else if (right_roster.root()->self == conflict.parent_name.first) { - // this node isn't attached in the merged roster and there - // isn't really a good name for it so report both the left - // and right names using a slightly different format + P(F("'%s' pivoted to root on the right") + % lca_parent_name); - file_path left_name, right_name; + file_path left_name; left_roster.get_name(conflict.nid, left_name); - right_roster.get_name(conflict.nid, right_name); - - shared_ptr lca_roster; - revision_id lca_rid; - file_path lca_name; - - adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); - lca_roster->get_name(conflict.nid, lca_name); - - P(F("conflict: content conflict on file '%s' from revision %s") - % lca_name % lca_rid); - P(F("content hash is %s on the left in file '%s'") - % conflict.left % left_name); - P(F("content hash is %s on the right in file '%s'") - % conflict.right % right_name); + if (parent_lca_roster->has_node(conflict.nid)) + { + P(F("'%s' renamed to '%s' on the left") + % lca_name % left_name); + } + else + { + P(F("'%s' added in revision %s on the left") + % left_name % lca_rid); + } } + else + I(false); } } void -roster_merge_result::report_attribute_conflicts(roster_t const & left_roster, - roster_t const & right_roster, - content_merge_adaptor & adaptor) const +roster_merge_result::report_directory_loop_conflicts(roster_t const & left_roster, + roster_t const & right_roster, + content_merge_adaptor & adaptor) const { - for (size_t i = 0; i < attribute_conflicts.size(); ++i) + MM(left_roster); + MM(right_roster); + + for (size_t i = 0; i < directory_loop_conflicts.size(); ++i) { - attribute_conflict const & conflict = attribute_conflicts[i]; + directory_loop_conflict const & conflict = directory_loop_conflicts[i]; MM(conflict); - string const & type = get_type(roster, conflict.nid); + I(!roster.is_attached(conflict.nid)); - if (roster.is_attached(conflict.nid)) - { - file_path name; - roster.get_name(conflict.nid, name); + file_path left_name, right_name, left_parent_name, right_parent_name; - P(F("conflict: multiple values for attribute '%s' on %s '%s'") - % conflict.key % type % name); + left_roster.get_name(conflict.nid, left_name); + right_roster.get_name(conflict.nid, right_name); - if (conflict.left.first) - P(F("set to '%s' on the left") % conflict.left.second); - else - P(F("deleted on the left")); + left_roster.get_name(conflict.parent_name.first, left_parent_name); + right_roster.get_name(conflict.parent_name.first, right_parent_name); - if (conflict.right.first) - P(F("set to '%s' on the right") % conflict.right.second); - else - P(F("deleted on the right")); - } - else - { - // this node isn't attached in the merged roster and there - // isn't really a good name for it so report both the left - // and right names using a slightly different format + shared_ptr lca_roster; + revision_id lca_rid; + file_path lca_name, lca_parent_name; - file_path left_name, right_name; - left_roster.get_name(conflict.nid, left_name); - right_roster.get_name(conflict.nid, right_name); + adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); + lca_roster->get_name(conflict.nid, lca_name); + lca_roster->get_name(conflict.parent_name.first, lca_parent_name); - shared_ptr lca_roster; - revision_id lca_rid; - file_path lca_name; + P(F("conflict: directory loop created")); - adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); - lca_roster->get_name(conflict.nid, lca_name); + if (left_name != lca_name) + P(F("'%s' renamed to '%s' on the left") + % lca_name % left_name); - P(F("conflict: multiple values for attribute '%s' on %s '%s' from revision %s") - % conflict.key % type % lca_name % lca_rid); + if (right_name != lca_name) + P(F("'%s' renamed to '%s' on the right") + % lca_name % right_name); - if (conflict.left.first) - P(F("set to '%s' on left %s '%s'") - % conflict.left.second % type % left_name); - else - P(F("deleted from left %s '%s'") - % type % left_name); + if (left_parent_name != lca_parent_name) + P(F("'%s' renamed to '%s' on the left") + % lca_parent_name % left_parent_name); - if (conflict.right.first) - P(F("set to '%s' on right %s '%s'") - % conflict.right.second % type % right_name); - else - P(F("deleted from right %s '%s'") - % type % right_name); - } + if (right_parent_name != lca_parent_name) + P(F("'%s' renamed to '%s' on the right") + % lca_parent_name % right_parent_name); } } @@ -345,6 +371,9 @@ roster_merge_result::report_orphaned_nod roster_t const & right_roster, content_merge_adaptor & adaptor) const { + MM(left_roster); + MM(right_roster); + for (size_t i = 0; i < orphaned_node_conflicts.size(); ++i) { orphaned_node_conflict const & conflict = orphaned_node_conflicts[i]; @@ -412,10 +441,47 @@ void } void +roster_merge_result::report_multiple_name_conflicts(roster_t const & left_roster, + roster_t const & right_roster, + content_merge_adaptor & adaptor) const +{ + MM(left_roster); + MM(right_roster); + + for (size_t i = 0; i < multiple_name_conflicts.size(); ++i) + { + multiple_name_conflict const & conflict = multiple_name_conflicts[i]; + MM(conflict); + + I(!roster.is_attached(conflict.nid)); + + file_path left_name, right_name; + + left_roster.get_name(conflict.nid, left_name); + right_roster.get_name(conflict.nid, right_name); + + shared_ptr lca_roster; + revision_id lca_rid; + file_path lca_name; + + adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); + lca_roster->get_name(conflict.nid, lca_name); + + P(F("conflict: multiple names for %s '%s' from revision %s") + % get_type(*lca_roster, conflict.nid) % lca_name % lca_rid); + P(F("renamed to '%s' on the left") % left_name); + P(F("renamed to '%s' on the right") % right_name); + } +} + +void roster_merge_result::report_duplicate_name_conflicts(roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor) const { + MM(left_roster); + MM(right_roster); + for (size_t i = 0; i < duplicate_name_conflicts.size(); ++i) { duplicate_name_conflict const & conflict = duplicate_name_conflicts[i]; @@ -487,195 +553,139 @@ void } void -roster_merge_result::report_directory_loop_conflicts(roster_t const & left_roster, - roster_t const & right_roster, - content_merge_adaptor & adaptor) const +roster_merge_result::report_attribute_conflicts(roster_t const & left_roster, + roster_t const & right_roster, + content_merge_adaptor & adaptor) const { - for (size_t i = 0; i < directory_loop_conflicts.size(); ++i) + MM(left_roster); + MM(right_roster); + + for (size_t i = 0; i < attribute_conflicts.size(); ++i) { - directory_loop_conflict const & conflict = directory_loop_conflicts[i]; + attribute_conflict const & conflict = attribute_conflicts[i]; MM(conflict); - I(!roster.is_attached(conflict.nid)); + string const & type = get_type(roster, conflict.nid); - file_path left_name, right_name, left_parent_name, right_parent_name; + if (roster.is_attached(conflict.nid)) + { + file_path name; + roster.get_name(conflict.nid, name); - left_roster.get_name(conflict.nid, left_name); - right_roster.get_name(conflict.nid, right_name); + P(F("conflict: multiple values for attribute '%s' on %s '%s'") + % conflict.key % type % name); - left_roster.get_name(conflict.parent_name.first, left_parent_name); - right_roster.get_name(conflict.parent_name.first, right_parent_name); + if (conflict.left.first) + P(F("set to '%s' on the left") % conflict.left.second); + else + P(F("deleted on the left")); - shared_ptr lca_roster; - revision_id lca_rid; - file_path lca_name, lca_parent_name; + if (conflict.right.first) + P(F("set to '%s' on the right") % conflict.right.second); + else + P(F("deleted on the right")); + } + else + { + // this node isn't attached in the merged roster and there + // isn't really a good name for it so report both the left + // and right names using a slightly different format - adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); - lca_roster->get_name(conflict.nid, lca_name); - lca_roster->get_name(conflict.parent_name.first, lca_parent_name); + file_path left_name, right_name; + left_roster.get_name(conflict.nid, left_name); + right_roster.get_name(conflict.nid, right_name); - P(F("conflict: directory loop created")); + shared_ptr lca_roster; + revision_id lca_rid; + file_path lca_name; - if (left_name != lca_name) - P(F("'%s' renamed to '%s' on the left") - % lca_name % left_name); + adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); + lca_roster->get_name(conflict.nid, lca_name); - if (right_name != lca_name) - P(F("'%s' renamed to '%s' on the right") - % lca_name % right_name); + P(F("conflict: multiple values for attribute '%s' on %s '%s' from revision %s") + % conflict.key % type % lca_name % lca_rid); - if (left_parent_name != lca_parent_name) - P(F("'%s' renamed to '%s' on the left") - % lca_parent_name % left_parent_name); + if (conflict.left.first) + P(F("set to '%s' on left %s '%s'") + % conflict.left.second % type % left_name); + else + P(F("deleted from left %s '%s'") + % type % left_name); - if (right_parent_name != lca_parent_name) - P(F("'%s' renamed to '%s' on the right") - % lca_parent_name % right_parent_name); + if (conflict.right.first) + P(F("set to '%s' on right %s '%s'") + % conflict.right.second % type % right_name); + else + P(F("deleted from right %s '%s'") + % type % right_name); + } } } void -roster_merge_result::report_invalid_name_conflicts(roster_t const & left_roster, +roster_merge_result::report_file_content_conflicts(roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor) const { - for (size_t i = 0; i < invalid_name_conflicts.size(); ++i) + MM(left_roster); + MM(right_roster); + + for (size_t i = 0; i < file_content_conflicts.size(); ++i) { - invalid_name_conflict const & conflict = invalid_name_conflicts[i]; + file_content_conflict const & conflict = file_content_conflicts[i]; MM(conflict); - I(!roster.is_attached(conflict.nid)); - - shared_ptr lca_roster, parent_lca_roster; - revision_id lca_rid, parent_lca_rid; - file_path lca_name, lca_parent_name; - - adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); - lca_roster->get_name(conflict.nid, lca_name); - lca_roster->get_name(conflict.parent_name.first, lca_parent_name); - - adaptor.get_ancestral_roster(conflict.parent_name.first, - parent_lca_rid, parent_lca_roster); - - P(F("conflict: invalid name _MTN in root directory")); - - if (left_roster.root()->self == conflict.parent_name.first) + if (roster.is_attached(conflict.nid)) { - P(F("'%s' pivoted to root on the left") - % lca_parent_name); + file_path name; + roster.get_name(conflict.nid, name); - file_path right_name; - right_roster.get_name(conflict.nid, right_name); - if (parent_lca_roster->has_node(conflict.nid)) - { - P(F("'%s' renamed to '%s' on the right") - % lca_name % right_name); - } - else - { - P(F("'%s' added in revision %s on the right") - % right_name % lca_rid); - } + P(F("conflict: content conflict on file '%s'") + % name); + P(F("content hash is %s on the left") % conflict.left); + P(F("content hash is %s on the right") % conflict.right); } - else if (right_roster.root()->self == conflict.parent_name.first) + else { - P(F("'%s' pivoted to root on the right") - % lca_parent_name); + // this node isn't attached in the merged roster and there + // isn't really a good name for it so report both the left + // and right names using a slightly different format - file_path left_name; + file_path left_name, right_name; left_roster.get_name(conflict.nid, left_name); - if (parent_lca_roster->has_node(conflict.nid)) - { - P(F("'%s' renamed to '%s' on the left") - % lca_name % left_name); - } - else - { - P(F("'%s' added in revision %s on the left") - % left_name % lca_rid); - } - } - else - I(false); - } -} + right_roster.get_name(conflict.nid, right_name); -void -roster_merge_result::report_missing_root_conflicts(roster_t const & left_roster, - roster_t const & right_roster, - content_merge_adaptor & adaptor) const -{ - if (missing_root_dir) - { - node_id left_root, right_root; - left_root = left_roster.root()->self; - right_root = right_roster.root()->self; - - P(F("conflict: missing root directory")); - - if (left_roster.has_node(right_root) && - !right_roster.has_node(left_root)) - { shared_ptr lca_roster; revision_id lca_rid; file_path lca_name; - adaptor.get_ancestral_roster(left_root, lca_rid, lca_roster); - lca_roster->get_name(left_root, lca_name); + adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster); + lca_roster->get_name(conflict.nid, lca_name); - P(F("directory '%s' pivoted to root on the left") % lca_name); - P(F("directory '%s' deleted on the right") % lca_name); + P(F("conflict: content conflict on file '%s' from revision %s") + % lca_name % lca_rid); + P(F("content hash is %s on the left in file '%s'") + % conflict.left % left_name); + P(F("content hash is %s on the right in file '%s'") + % conflict.right % right_name); } - else if (!left_roster.has_node(right_root) && - right_roster.has_node(left_root)) - { - shared_ptr lca_roster; - revision_id lca_rid; - file_path lca_name; - - adaptor.get_ancestral_roster(right_root, lca_rid, lca_roster); - lca_roster->get_name(right_root, lca_name); - - P(F("directory '%s' pivoted to root on the right") % lca_name); - P(F("directory '%s' deleted on the left") % lca_name); - } - else if (!left_roster.has_node(right_root) && - !right_roster.has_node(left_root)) - { - shared_ptr left_lca_roster, right_lca_roster; - revision_id left_lca_rid, right_lca_rid; - file_path left_lca_name, right_lca_name; - - adaptor.get_ancestral_roster(left_root, left_lca_rid, - left_lca_roster); - adaptor.get_ancestral_roster(right_root, right_lca_rid, - right_lca_roster); - - left_lca_roster->get_name(left_root, left_lca_name); - right_lca_roster->get_name(right_root, right_lca_name); - - P(F("directory '%s' pivoted to root on the left") % left_lca_name); - P(F("directory '%s' deleted on the right") % left_lca_name); - - P(F("directory '%s' deleted on the left") % right_lca_name); - P(F("directory '%s' pivoted to root on the right") % right_lca_name); - } - else - I(false); } } void roster_merge_result::clear() { - multiple_name_conflicts.clear(); - file_content_conflicts.clear(); - attribute_conflicts.clear(); + missing_root_dir = false; + invalid_name_conflicts.clear(); + directory_loop_conflicts.clear(); + orphaned_node_conflicts.clear(); + multiple_name_conflicts.clear(); duplicate_name_conflicts.clear(); - directory_loop_conflicts.clear(); - invalid_name_conflicts.clear(); - missing_root_dir = false; + + attribute_conflicts.clear(); + file_content_conflicts.clear(); + roster = roster_t(); } ============================================================ --- roster_merge.hh 9110ec12f5d3d21a193067a1937b8d7c2d750546 +++ roster_merge.hh 4e12424804e19a3ad5c353d6bbb7b0c779be4519 @@ -15,61 +15,57 @@ #include "roster.hh" #include "sanity.hh" -// our general strategy is to return a (possibly insane) roster, and a list of -// conflicts encountered in that roster. Each conflict encountered in merging -// the roster creates an entry in this list. +// interactions between conflict types: +// node rename conflicts never participate in structural conflicts +// (e.g., merge , could be +// considered to have two conflicts -- 'a' being renamed to both 'foo' and +// 'bar', and 'a' and 'b' both being renamed to 'bar'. Only the former +// occurs; 'b' merges cleanly and will be named 'bar' in the resulting +// manifest.) +// -// nodes with multiple name conflicts are left detached in the resulting -// roster, with null parent and name fields. -// note that it is possible that the parent node on the left, the right, or -// both, no longer exist in the merged roster. also note that it is possible -// that on one or both sides, they do exist, but already have an entry with -// the given name. -struct multiple_name_conflict +// renaming the root dir allows these: +// -- _MTN in root +// -- missing root directory + +// this is a node that cleanly merged to some name, but that name was somehow +// forbidden. (Currently, the only forbidden name is "_MTN" in the root +// directory.) +struct invalid_name_conflict { node_id nid; - multiple_name_conflict(node_id nid) : nid(nid) {} - std::pair left, right; + std::pair parent_name; }; -// files with content conflicts are left attached in resulting tree (unless -// detached for some other reason), but with a null content hash. -struct file_content_conflict +struct directory_loop_conflict { node_id nid; - file_content_conflict(node_id nid) : nid(nid) {} - file_id left, right; + std::pair parent_name; }; -// nodes with attribute conflicts are left attached in the resulting tree (unless -// detached for some other reason), but with the given attribute left out of -// their full_attr_map_t. Note that this doesn't actually leave the resulting -// roster insane (FIXME: we could put an invalid attr value in instead, like a -// pair (false, "foo") (since the second value can only be non-null if the -// first is 'true'). Should we do this?) -struct attribute_conflict +// orphaned nodes always merged their name cleanly, so we simply put that name +// here. the node in the resulting roster is detached. +struct orphaned_node_conflict { node_id nid; - attribute_conflict(node_id nid) : nid(nid) {} - attr_key key; // attr_name? - std::pair left, right; + std::pair parent_name; }; -// interactions between conflict types: -// node rename conflicts never participate in structural conflicts -// (e.g., merge , could be -// considered to have two conflicts -- 'a' being renamed to both 'foo' and -// 'bar', and 'a' and 'b' both being renamed to 'bar'. Only the former -// occurs; 'b' merges cleanly and will be named 'bar' in the resulting -// manifest.) -// +// our general strategy is to return a (possibly insane) roster, and a list of +// conflicts encountered in that roster. Each conflict encountered in merging +// the roster creates an entry in this list. -// orphaned nodes always merged their name cleanly, so we simply put that name -// here. the node in the resulting roster is detached. -struct orphaned_node_conflict +// nodes with multiple name conflicts are left detached in the resulting +// roster, with null parent and name fields. +// note that it is possible that the parent node on the left, the right, or +// both, no longer exist in the merged roster. also note that it is possible +// that on one or both sides, they do exist, but already have an entry with +// the given name. +struct multiple_name_conflict { node_id nid; - std::pair parent_name; + multiple_name_conflict(node_id nid) : nid(nid) {} + std::pair left, right; }; // this is when two distinct nodes want to have the same name. these nodes @@ -92,54 +88,65 @@ struct duplicate_name_conflict std::pair parent_name; }; -struct directory_loop_conflict +// nodes with attribute conflicts are left attached in the resulting tree (unless +// detached for some other reason), but with the given attribute left out of +// their full_attr_map_t. Note that this doesn't actually leave the resulting +// roster insane (FIXME: we could put an invalid attr value in instead, like a +// pair (false, "foo") (since the second value can only be non-null if the +// first is 'true'). Should we do this?) +struct attribute_conflict { node_id nid; - std::pair parent_name; + attribute_conflict(node_id nid) : nid(nid) {} + attr_key key; // attr_name? + std::pair left, right; }; -// renaming the root dir allows these: -// -- _MTN in root -// -- missing root directory - -// this is a node that cleanly merged to some name, but that name was somehow -// forbidden. (Currently, the only forbidden name is "_MTN" in the root -// directory.) -struct invalid_name_conflict +// files with content conflicts are left attached in resulting tree (unless +// detached for some other reason), but with a null content hash. +struct file_content_conflict { node_id nid; - std::pair parent_name; + file_content_conflict(node_id nid) : nid(nid) {} + file_id left, right; }; + +template <> void dump(invalid_name_conflict const & conflict, std::string & out); +template <> void dump(directory_loop_conflict const & conflict, std::string & out); + +template <> void dump(orphaned_node_conflict const & conflict, std::string & out); template <> void dump(multiple_name_conflict const & conflict, std::string & out); -template <> void dump(file_content_conflict const & conflict, std::string & out); +template <> void dump(duplicate_name_conflict const & conflict, std::string & out); + template <> void dump(attribute_conflict const & conflict, std::string & out); -template <> void dump(orphaned_node_conflict const & conflict, std::string & out); -template <> void dump(duplicate_name_conflict const & conflict, std::string & out); -template <> void dump(directory_loop_conflict const & conflict, std::string & out); -template <> void dump(invalid_name_conflict const & conflict, std::string & out); +template <> void dump(file_content_conflict const & conflict, std::string & out); struct roster_merge_result { // three main types of conflicts - // - content conflicts - // - attribute conflicts // - structural conflicts (which have the following subtypes) + // - missing root directory + // - invalid name conflicts // - duplicate name conflicts + // - orphaned node conflicts // - multiple name conflicts - // - orphaned node conflicts // - directory loop conflicts - // - illegal name conflicts - // - missing root conflicts + // - attribute conflicts + // - file content conflicts + bool missing_root_dir; + std::vector invalid_name_conflicts; + std::vector directory_loop_conflicts; + + std::vector orphaned_node_conflicts; std::vector multiple_name_conflicts; - std::vector file_content_conflicts; + std::vector duplicate_name_conflicts; + std::vector attribute_conflicts; - std::vector orphaned_node_conflicts; - std::vector duplicate_name_conflicts; - std::vector directory_loop_conflicts; - std::vector invalid_name_conflicts; - bool missing_root_dir; + std::vector file_content_conflicts; + + // this roster is sane if is_clean() returns true roster_t roster; bool is_clean() const; @@ -147,30 +154,32 @@ struct roster_merge_result bool has_non_content_conflicts() const; void log_conflicts() const; - void report_multiple_name_conflicts(roster_t const & left, - roster_t const & right, - content_merge_adaptor & adaptor) const; - void report_file_content_conflicts(roster_t const & left, + void report_missing_root_conflicts(roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor) const; - void report_attribute_conflicts(roster_t const & left, - roster_t const & right, - content_merge_adaptor & adaptor) const; + void report_invalid_name_conflicts(roster_t const & left, + roster_t const & right, + content_merge_adaptor & adaptor) const; + void report_directory_loop_conflicts(roster_t const & left, + roster_t const & right, + content_merge_adaptor & adaptor) const; + void report_orphaned_node_conflicts(roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor) const; + void report_multiple_name_conflicts(roster_t const & left, + roster_t const & right, + content_merge_adaptor & adaptor) const; void report_duplicate_name_conflicts(roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor) const; - void report_directory_loop_conflicts(roster_t const & left, - roster_t const & right, - content_merge_adaptor & adaptor) const; - void report_invalid_name_conflicts(roster_t const & left, + + void report_attribute_conflicts(roster_t const & left, + roster_t const & right, + content_merge_adaptor & adaptor) const; + void report_file_content_conflicts(roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor) const; - void report_missing_root_conflicts(roster_t const & left, - roster_t const & right, - content_merge_adaptor & adaptor) const; void clear(); }; ============================================================ --- tests/conflict_messages/__driver__.lua 32d64b7bea2cf1e7c1c98ad6a1579762a4f6e5b1 +++ tests/conflict_messages/__driver__.lua c2eecfd2d5bb9786bf10e0710a3e87d9c94f898e @@ -1,161 +1,130 @@ mtn_setup() mtn_setup() -- this test creates the various non-content conflict cases -- and attempts to merge them to check the various messages +-- missing root conflict --- multiple name conflict - remove("_MTN") -check(mtn("setup", ".", "--branch", "multiple"), 0, false, false) +check(mtn("setup", ".", "--branch", "missing"), 0, false, false) -addfile("foo", "multiple foo") -commit("multiple") +mkdir("foo") +addfile("foo/foo", "missing foofoo") +commit("missing") + base = base_revision() -check(mtn("mv", "foo", "bar"), 0, false, false) -commit("multiple") -first = base_revision() +check(mtn("co", "--branch", "missing", "missing"), 0, false, false) +check(indir("missing", mtn("pivot_root", "foo", "bar")), 0, true, true) +--check(indir("missing", mtn("drop", "--recursive", "bar")), 0, true, true) +check(indir("missing", mtn("commit", "--message", "commit")), 0, false, false) -revert_to(base) +first = indir("missing", {base_revision})[1]() -check(mtn("mv", "foo", "baz"), 0, false, false) +check(mtn("drop", "--recursive", "foo"), 0, false, false) check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: multiple names", "stderr")) +check(qgrep("conflict: missing root directory", "stderr")) -commit("multiple") +commit("missing") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: multiple names", "stderr")) +check(qgrep("conflict: missing root directory", "stderr")) -check(mtn("merge", "--branch", "multiple"), 1, false, true) -check(qgrep("conflict: multiple names", "stderr")) +check(mtn("merge", "--branch", "missing"), 1, false, true) +check(qgrep("conflict: missing root directory", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: multiple names", "stderr")) +check(qgrep("conflict: missing root directory", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: multiple names", "stderr")) +check(qgrep("conflict: missing root directory", "stderr")) --- duplicate name conflict (adds) -remove("_MTN") -check(mtn("setup", ".", "--branch", "duplicate-adds"), 0, false, false) +-- invalid name add -addfile("foo", "duplicate add foo") -commit("duplicate-adds") -base = base_revision() - -addfile("xxx", "duplicate add xxx") -commit("duplicate-adds") - -check(mtn("mv", "xxx", "bar"), 0, false, false) ---addfile("bar", "duplicate add bar1") -commit("duplicate-adds") -first = base_revision() - -revert_to(base) - -addfile("bar", "duplicate add bar2") - -check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) - -commit("duplicate-adds") -second = base_revision() - -check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: duplicate name", "stderr")) - -check(mtn("merge", "--branch", "duplicate-adds"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) - -check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) - -check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) - - --- duplicate name conflict (renames) - remove("_MTN") -check(mtn("setup", ".", "--branch", "duplicate-renames"), 0, false, false) +check(mtn("setup", ".", "--branch", "invalid-add"), 0, false, false) -addfile("foo", "duplicate rename foo") -addfile("bar", "duplicate rename bar") +mkdir("foo") +addfile("foo/foo", "invalid add foofoo") +commit("invalid-add") -commit("duplicate-renames") base = base_revision() -check(mtn("mv", "foo", "abc"), 0, false, false) -commit("duplicate-renames") -first = base_revision() +check(mtn("co", "--branch", "invalid-add", "invalid"), 0, false, false) +check(indir("invalid", mtn("pivot_root", "foo", "bar")), 0, true, true) +check(indir("invalid", mtn("commit", "--message", "commit")), 0, false, false) -revert_to(base) +first = indir("invalid", {base_revision})[1]() -check(mtn("mv", "bar", "abc"), 0, false, false) +mkdir("foo/_MTN") +addfile("foo/_MTN/foo", "invalid foo") +addfile("foo/_MTN/bar", "invalid bar") check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) -commit("duplicate-renames") +commit("invalid-add") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) -check(mtn("merge", "--branch", "duplicate-renames"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(mtn("merge", "--branch", "invalid-add"), 1, false, true) +check(qgrep("conflict: invalid name", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) --- duplicate name conflict (add-rename) +-- invalid name rename remove("_MTN") -check(mtn("setup", ".", "--branch", "duplicate-add-rename"), 0, false, false) +remove("invalid") +check(mtn("setup", ".", "--branch", "invalid-rename"), 0, false, false) -addfile("foo", "duplicate add rename foo") +mkdir("foo") +mkdir("bad") +addfile("foo/foo", "invalid rename foofoo") +addfile("bad/_MTN", "invalid bar") +commit("invalid-rename") -commit("duplicate-add-rename") base = base_revision() -check(mtn("mv", "foo", "bar"), 0, false, false) -commit("duplicate-add-rename") -first = base_revision() +check(mtn("co", "--branch", "invalid-rename", "invalid"), 0, false, false) +check(indir("invalid", mtn("pivot_root", "foo", "bar")), 0, true, true) +check(indir("invalid", mtn("commit", "--message", "commit")), 0, false, false) +first = indir("invalid", {base_revision})[1]() -revert_to(base) +check(mtn("mv", "bad/_MTN", "foo/_MTN"), 0, false, false) -addfile("bar", "convervent add rename bar") - check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) -commit("duplicate-add-rename") +commit("invalid-rename") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) -check(mtn("merge", "--branch", "duplicate-add-rename"), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(mtn("merge", "--branch", "invalid-rename"), 1, false, true) +check(qgrep("conflict: invalid name", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: duplicate name", "stderr")) +check(qgrep("conflict: invalid name", "stderr")) + -- directory loop conflict remove("_MTN") @@ -198,6 +167,7 @@ check(qgrep("conflict: directory loop", check(qgrep("conflict: directory loop", "stderr")) + -- orphaned add remove("_MTN") @@ -286,124 +256,157 @@ check(qgrep("conflict: orphaned file", " check(qgrep("conflict: orphaned file", "stderr")) --- invalid name add +-- multiple name conflict + remove("_MTN") -check(mtn("setup", ".", "--branch", "invalid-add"), 0, false, false) +check(mtn("setup", ".", "--branch", "multiple"), 0, false, false) -mkdir("foo") -addfile("foo/foo", "invalid add foofoo") -commit("invalid-add") +addfile("foo", "multiple foo") +commit("multiple") +base = base_revision() +check(mtn("mv", "foo", "bar"), 0, false, false) +commit("multiple") +first = base_revision() + +revert_to(base) + +check(mtn("mv", "foo", "baz"), 0, false, false) + +check(mtn("update", "--debug"), 1, false, true) +check(qgrep("conflict: multiple names", "stderr")) + +commit("multiple") +second = base_revision() + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep("conflict: multiple names", "stderr")) + +check(mtn("merge", "--branch", "multiple"), 1, false, true) +check(qgrep("conflict: multiple names", "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep("conflict: multiple names", "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep("conflict: multiple names", "stderr")) + + + +-- duplicate name conflict (adds) + +remove("_MTN") +check(mtn("setup", ".", "--branch", "duplicate-adds"), 0, false, false) + +addfile("foo", "duplicate add foo") +commit("duplicate-adds") base = base_revision() -check(mtn("co", "--branch", "invalid-add", "invalid"), 0, false, false) -check(indir("invalid", mtn("pivot_root", "foo", "bar")), 0, true, true) -check(indir("invalid", mtn("commit", "--message", "commit")), 0, false, false) +addfile("xxx", "duplicate add xxx") +commit("duplicate-adds") -first = indir("invalid", {base_revision})[1]() +check(mtn("mv", "xxx", "bar"), 0, false, false) +--addfile("bar", "duplicate add bar1") +commit("duplicate-adds") +first = base_revision() -mkdir("foo/_MTN") -addfile("foo/_MTN/foo", "invalid foo") -addfile("foo/_MTN/bar", "invalid bar") +revert_to(base) +addfile("bar", "duplicate add bar2") + check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -commit("invalid-add") +commit("duplicate-adds") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -check(mtn("merge", "--branch", "invalid-add"), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(mtn("merge", "--branch", "duplicate-adds"), 1, false, true) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) --- invalid name rename +-- duplicate name conflict (renames) remove("_MTN") -remove("invalid") -check(mtn("setup", ".", "--branch", "invalid-rename"), 0, false, false) +check(mtn("setup", ".", "--branch", "duplicate-renames"), 0, false, false) -mkdir("foo") -mkdir("bad") -addfile("foo/foo", "invalid rename foofoo") -addfile("bad/_MTN", "invalid bar") -commit("invalid-rename") +addfile("foo", "duplicate rename foo") +addfile("bar", "duplicate rename bar") +commit("duplicate-renames") base = base_revision() -check(mtn("co", "--branch", "invalid-rename", "invalid"), 0, false, false) -check(indir("invalid", mtn("pivot_root", "foo", "bar")), 0, true, true) -check(indir("invalid", mtn("commit", "--message", "commit")), 0, false, false) -first = indir("invalid", {base_revision})[1]() +check(mtn("mv", "foo", "abc"), 0, false, false) +commit("duplicate-renames") +first = base_revision() -check(mtn("mv", "bad/_MTN", "foo/_MTN"), 0, false, false) +revert_to(base) +check(mtn("mv", "bar", "abc"), 0, false, false) + check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -commit("invalid-rename") +commit("duplicate-renames") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -check(mtn("merge", "--branch", "invalid-rename"), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(mtn("merge", "--branch", "duplicate-renames"), 1, false, true) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: invalid name", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) +-- duplicate name conflict (add-rename) --- missing root conflict - remove("_MTN") -check(mtn("setup", ".", "--branch", "missing"), 0, false, false) +check(mtn("setup", ".", "--branch", "duplicate-add-rename"), 0, false, false) -mkdir("foo") -addfile("foo/foo", "missing foofoo") -commit("missing") +addfile("foo", "duplicate add rename foo") +commit("duplicate-add-rename") base = base_revision() -check(mtn("co", "--branch", "missing", "missing"), 0, false, false) -check(indir("missing", mtn("pivot_root", "foo", "bar")), 0, true, true) ---check(indir("missing", mtn("drop", "--recursive", "bar")), 0, true, true) -check(indir("missing", mtn("commit", "--message", "commit")), 0, false, false) +check(mtn("mv", "foo", "bar"), 0, false, false) +commit("duplicate-add-rename") +first = base_revision() -first = indir("missing", {base_revision})[1]() +revert_to(base) -check(mtn("drop", "--recursive", "foo"), 0, false, false) +addfile("bar", "convervent add rename bar") check(mtn("update", "--debug"), 1, false, true) -check(qgrep("conflict: missing root directory", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -commit("missing") +commit("duplicate-add-rename") second = base_revision() check(mtn("show_conflicts", first, second), 0, false, true) -check(qgrep("conflict: missing root directory", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) -check(mtn("merge", "--branch", "missing"), 1, false, true) -check(qgrep("conflict: missing root directory", "stderr")) +check(mtn("merge", "--branch", "duplicate-add-rename"), 1, false, true) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) -check(qgrep("conflict: missing root directory", "stderr")) +check(qgrep("conflict: duplicate name", "stderr")) check(mtn("merge_into_workspace", first), 1, false, true) -check(qgrep("conflict: missing root directory", "stderr")) +check(qgrep("conflict: duplicate name", "stderr"))