# # # patch "merge.cc" # from [9ec8924bac7082af2a9fe16889f874a5f5a0c67c] # to [0aa7054c267c833ca753a85ea1bad3ca9997d667] # # patch "roster.cc" # from [932a1eaa645faae04e0523ac971c997bcdeacb4f] # to [70af963b64dd2729562ca29ae346392ff299c108] # # patch "roster.hh" # from [0da49ea4d1bda34fe2875b0e45add9b6918f2df4] # to [0a976adbca1e66062d5bd9fcfb063eef07427f73] # # patch "roster_merge.cc" # from [b53b18bbe337ad0e4577a577f5cf5c716d621b85] # to [992ee1d3932808bd0e059c4e8f984c4fcd39b586] # # patch "tests/resolve_duplicate_name_conflict/__driver__.lua" # from [bd0f02e6897beeb64dba8119288df66a3ef1128f] # to [8f477bd075f50532cdb3a519e136122443596aa5] # ============================================================ --- merge.cc 9ec8924bac7082af2a9fe16889f874a5f5a0c67c +++ merge.cc 0aa7054c267c833ca753a85ea1bad3ca9997d667 @@ -156,6 +156,7 @@ resolve_merge_conflicts(lua_hooks & lua, // If there aren't any we can't resolve, resolve the ones we can. result.resolve_duplicate_name_conflicts(lua, left_roster, right_roster, adaptor); + // FIXME: need to resolve content conflicts here } else { @@ -246,7 +247,7 @@ store_roster_merge_result(database & db, { I(result.is_clean()); roster_t & merged_roster = result.roster; - merged_roster.check_sane(); + merged_roster.check_sane(true); // sutured nodes have temp node ids revision_t merged_rev; merged_rev.made_for = made_for_database; ============================================================ --- roster.cc 932a1eaa645faae04e0523ac971c997bcdeacb4f +++ roster.cc 70af963b64dd2729562ca29ae346392ff299c108 @@ -814,7 +814,11 @@ roster_t::drop_detached_node(node_id nid // old_locations, all those that used to be in the tree do. and you should // only ever be dropping nodes that were detached, not nodes that you just // created and that have never been attached. - safe_erase(old_locations, nid); + + // Update; resolving a duplicate name conflict via suture requires + // dropping nodes that were never attached. So we erase the key without + // checking whether it was present. FIXME: clean up these comments. + old_locations.erase(nid); } ============================================================ --- roster.hh 0da49ea4d1bda34fe2875b0e45add9b6918f2df4 +++ roster.hh 0a976adbca1e66062d5bd9fcfb063eef07427f73 @@ -270,13 +270,16 @@ private: // definitely make sense to move this checking into editable_tree. For now, // though, no such functionality is planned, so we'll see what happens. // + // Update; now we are adding precisely these operations! workaround; allow + // deleting a detached node with no entry in old_locations + // // The implementation itself uses the map old_locations. A node can be in // the following states: // -- attached, no entry in old_locations map // -- detached, no entry in old_locations map // -- create_dir_node, create_file_node put a node into this state // -- a node in this state can be attached, anywhere, but may not be - // deleted. + // deleted. Update; can now be deleted. // -- detached, an entry in old_locations map // -- detach_node puts a node into this state // -- a node in this state can be attached anywhere _except_ the ============================================================ --- roster_merge.cc b53b18bbe337ad0e4577a577f5cf5c716d621b85 +++ roster_merge.cc 992ee1d3932808bd0e059c4e8f984c4fcd39b586 @@ -1563,10 +1563,9 @@ static void } // parse_resolve_conflicts_opts static void -set_new_name_in_roster (lua_hooks & lua, +attach_node (lua_hooks & lua, roster_t & new_roster, node_id nid, - file_path const source_path, file_path const target_path) { // Simplified from workspace::perform_rename in work.cc @@ -1577,8 +1576,6 @@ set_new_name_in_roster (lua_hooks & lua, N(new_roster.has_node(target_path.dirname()), F("directory %s does not exist or is unknown") % target_path.dirname()); - P(F("renaming %s to %s") % source_path % target_path); - new_roster.attach_node (nid, target_path); node_t node = new_roster.get_node (nid); @@ -1587,7 +1584,7 @@ set_new_name_in_roster (lua_hooks & lua, ++attr) lua.hook_apply_attribute (attr->first(), target_path, attr->second.second()); -} // set_new_name_in_roster +} // attach_node void roster_merge_result::resolve_duplicate_name_conflicts(lua_hooks & lua, @@ -1599,39 +1596,81 @@ roster_merge_result::resolve_duplicate_n MM(right_roster); MM(this->roster); // New roster + // Conflict nodes are present but detached (without filenames) in the new + // roster. The resolution is either to suture the two files together, or to + // rename one or both. + + // This is the only conflict resolution that needs to create new nodes, so + // we can declare the node id source here. + temp_node_id_source nis; + for (std::vector::const_iterator i = duplicate_name_conflicts.begin(); i != duplicate_name_conflicts.end(); ++i) { - // FIXME: share this code with above? duplicate_name_conflict const & conflict = *i; MM(conflict); node_id left_nid = conflict.left_nid; node_id right_nid= conflict.right_nid; - // conflict nodes are present but detached (without filenames) in new roster - file_path left_name, right_name; left_roster.get_name(left_nid, left_name); right_roster.get_name(right_nid, right_name); - // end FIXME: - - // The resolution is either to suture the two files together, or to rename one or both. - switch (conflict.left_resolution.first) { case resolve_conflicts::suture: I(conflict.right_resolution.first == resolve_conflicts::suture); - // FIXME: do the suturing, mark conflict as resolved in result roster - I(false); + P(F("suturing %s, %s into %s") % left_name % right_name % conflict.left_resolution.second); + + // Create a single new node, delete the two old ones. FIXME: need to + // record the links between the nodes somewhere. + { + node_id new_nid; + + file_path const new_file_name = conflict.left_resolution.second; + + if (is_dir_t(left_roster.get_node (left_nid))) + new_nid = roster.create_dir_node (nis); + else + { + file_t const left_node = downcast_to_file_t(left_roster.get_node (left_nid)); + file_t const right_node = downcast_to_file_t(right_roster.get_node (right_nid)); + + N(path::file == get_path_status(new_file_name), + F("%s does not exist or is a directory") % new_file_name); + + file_id const & left_file_id = left_node->content; + file_id const & right_file_id = right_node->content; + file_id new_file_id; + data new_raw_data; + read_data (new_file_name, new_raw_data); + file_data new_data (new_raw_data); + file_data left_data, right_data; + + adaptor.get_version(left_file_id, left_data); + adaptor.get_version(right_file_id, right_data); + calculate_ident (new_data, new_file_id); + + new_nid = roster.create_file_node (new_file_id, nis); + + adaptor.record_merge(left_file_id, right_file_id, new_file_id, left_data, right_data, new_data); + } + + attach_node (lua, roster, new_nid, new_file_name); + + roster.drop_detached_node(left_nid); + roster.drop_detached_node(right_nid); + + } break; case resolve_conflicts::rename: - set_new_name_in_roster (lua, this->roster, left_nid, left_name, conflict.left_resolution.second); + P(F("renaming %s to %s") % left_name % conflict.left_resolution.second); + attach_node (lua, this->roster, left_nid, conflict.left_resolution.second); break; case resolve_conflicts::none: @@ -1651,7 +1690,8 @@ roster_merge_result::resolve_duplicate_n break; case resolve_conflicts::rename: - set_new_name_in_roster (lua, this->roster, right_nid, right_name, conflict.right_resolution.second); + P(F("renaming %s to %s") % right_name % conflict.right_resolution.second); + attach_node (lua, this->roster, right_nid, conflict.right_resolution.second); break; case resolve_conflicts::none: ============================================================ --- tests/resolve_duplicate_name_conflict/__driver__.lua bd0f02e6897beeb64dba8119288df66a3ef1128f +++ tests/resolve_duplicate_name_conflict/__driver__.lua 8f477bd075f50532cdb3a519e136122443596aa5 @@ -88,9 +88,20 @@ check(mtn("merge", "--resolve-conflicts- -- This succeeds check(mtn("merge", "--resolve-conflicts-file=_MTN/conflicts"), 0, true, true) -check(mtn("update"), 0, false, false) +-- update fails if thermostat.c is missing, and if +-- thermostat-honeywell.c, thermostat-westinghouse.c are in the way. +-- So clean that up first. FIXME: update needs --ignore-missing, +-- --overwrite-ws or something. +check(mtn("revert", "--missing"), 0, false, false) +remove ("thermostat-honeywell.c") +remove ("thermostat-westinghouse.c") +check(mtn("update"), 0, true, true) --- Verify the merged checkout.sh got committed +-- Verify file contents +check("thermostat westinghouse" == readfile("thermostat-westinghouse.c")) +check("thermostat honeywell" == readfile("thermostat-honeywell.c")) + +-- This currently fails; the merge during update first adds then drops +-- checkout.sh. Need to change diediedie. check("checkout.sh merged" == readfile("checkout.sh")) - -- end of file