# # patch "ChangeLog" # from [1218b5bd0ee80fb7f90b1aa1e64a29d7ae6f3c77] # to [1486b1d2adf0958d9f9b7f79e1a6083d49d852da] # # patch "change_set.cc" # from [acf229c928d5a98c12d51fae465303b2ab9a1454] # to [dca63f6f968128d67da90a44a1c2bb09e0d9c0ed] # # patch "change_set.hh" # from [d530354cb57477ef5e3b9e961346861c61508cba] # to [f8e06d50c8b82b973b2de70137f70171dac6120d] # # patch "commands.cc" # from [e3432b9b21954ad6f3628a6afbff3e6328e3cea1] # to [5c0ba1f50fee0b05d49e0f705875b563d03efcfd] # # patch "pcdv.cc" # from [98142af12f61b816c38685f185fe3eaae0447dca] # to [7bb131b0c0268cae51eadc9d22a11d7ec6d7ad11] # # patch "pcdv.hh" # from [f650089a46f20388d4d5976d906389af52c23934] # to [42ce2c9da4c7a1e77b1200885b6d3fc579592c69] # ======================================================================== --- ChangeLog 1218b5bd0ee80fb7f90b1aa1e64a29d7ae6f3c77 +++ ChangeLog 1486b1d2adf0958d9f9b7f79e1a6083d49d852da @@ -1,3 +1,12 @@ +2005-08-16 Timothy Brownawell + + * change_set.{cc,hh}: Replace tree merger with the one in pcdv.{cc,hh}. + DOES NOT pass ./testsuite (13 failures, 1 unexpected pass) + REMOVE disjoint_merge_test() (no longer applicable in + this form, and the same functionality is checked in tests/ .) + * commands.cc: New merger takes different arguments; update calls. + * pcdv.{cc,hh}: Handle sutures better. + 2005-08-10 Timothy Brownawell * pcdv.{cc,hh} (tree merger): Teach it about file suturing. ======================================================================== --- change_set.cc acf229c928d5a98c12d51fae465303b2ab9a1454 +++ change_set.cc dca63f6f968128d67da90a44a1c2bb09e0d9c0ed @@ -30,6 +30,10 @@ #include "smap.hh" #include "path_component.hh" +#include "pcdv.hh" +#include "database.hh" +#include "revision.hh" + // our analyses in this file happen on one of two families of // related structures: a path_analysis or a directory_map. // @@ -1633,7 +1637,7 @@ // begin stuff related to merging - +/* static void extend_renumbering_via_added_files(path_analysis const & a, path_analysis const & b, @@ -1676,7 +1680,7 @@ } } } - +*/ static bool find_item(tid t, path_state const & ps, path_item & item) @@ -1687,7 +1691,7 @@ item = path_state_item(i); return true; } - +/* static bool find_items(tid t, path_analysis const & pa, path_item & first, path_item & second) @@ -2034,12 +2038,24 @@ } } +*/ +struct itempaths +{ + file_path anc; + file_path left; + file_path right; + file_path merged; + itempaths(file_path const & a, file_path const & l, + file_path const & r, file_path const & m): + anc(a), left(l), right(r), merged(m) + {} + itempaths() + {} +}; + static void -merge_deltas(file_path const & anc_path, - file_path const & left_path, - file_path const & right_path, - file_path const & path_in_merged, +merge_deltas(itempaths const & paths, std::map & merge_finalists, file_id const & anc, file_id const & left, @@ -2047,56 +2063,55 @@ file_id & finalist, merge_provider & merger) { - std::map::const_iterator i = merge_finalists.find(path_in_merged); + std::map::const_iterator + i = merge_finalists.find(paths.merged); if (i != merge_finalists.end()) { L(F("reusing merge resolution '%s' : '%s' -> '%s'\n") - % path_in_merged % anc % i->second); + % paths.merged % anc % i->second); finalist = i->second; } else { if (null_id(anc)) { - N(merger.try_to_merge_files(left_path, right_path, path_in_merged, left, right, finalist), + N(merger.try_to_merge_files(paths.left, paths.right, paths.merged, + left, right, finalist), F("merge of '%s' : '%s' vs. '%s' (no common ancestor) failed") - % path_in_merged % left % right); + % paths.merged % left % right); } else { - N(merger.try_to_merge_files(anc_path, left_path, right_path, path_in_merged, + N(merger.try_to_merge_files(paths.anc, paths.left, + paths.right, paths.merged, anc, left, right, finalist), F("merge of '%s' : '%s' -> '%s' vs '%s' failed") - % path_in_merged % anc % left % right); + % paths.merged % anc % left % right); } L(F("merge of '%s' : '%s' -> '%s' vs '%s' resolved to '%s'\n") - % path_in_merged % anc % left % right % finalist); + % paths.merged % anc % left % right % finalist); - merge_finalists.insert(std::make_pair(path_in_merged, finalist)); + merge_finalists.insert(std::make_pair(paths.merged, finalist)); } } static void project_missing_deltas(change_set const & a, change_set const & b, - path_analysis const & a_analysis, - path_analysis const & b_analysis, - path_analysis const & a_merged_analysis, + std::vector const & pathset, change_set & b_merged, merge_provider & merger, std::map & merge_finalists) { - directory_map a_second_map, b_first_map, a_merged_first_map; - build_directory_map(a_analysis.second, a_second_map); - build_directory_map(b_analysis.first, b_first_map); - build_directory_map(a_merged_analysis.first, a_merged_first_map); + std::map pathsmap; + for (std::vector::const_iterator i = pathset.begin(); + i != pathset.end(); ++i) + pathsmap.insert(make_pair((*i).left, *i)); - for (change_set::delta_map::const_iterator i = a.deltas.begin(); + for (change_set::delta_map::const_iterator i = a.deltas.begin(); i != a.deltas.end(); ++i) { - file_path path_in_merged, path_in_anc, path_in_b_second; - // we have a fork like this: // // @@ -2105,31 +2120,18 @@ // +--> [b2] // // and we have a delta applied to a file in a2. we want to - // figure out what to call this delta's path in b2. this means - // reconstructing it in a1==b1, then reconstructing it *again* - // in b2. + // figure out what to call this delta's path in b2. - // first work out what the path in a.first == b.first is - reconstruct_path(delta_entry_path(i), a_second_map, - a_analysis.first, path_in_anc); + itempaths const & paths = pathsmap[delta_entry_path(i)]; - // first work out what the path in b.second is - reconstruct_path(path_in_anc, b_first_map, - b_analysis.second, path_in_b_second); - - // then work out what the path in merged is - reconstruct_path(delta_entry_path(i), a_merged_first_map, - a_merged_analysis.second, path_in_merged); - // now check to see if there was a delta on the b.second name in b - change_set::delta_map::const_iterator j = b.deltas.find(path_in_b_second); + change_set::delta_map::const_iterator j = b.deltas.find(paths.right); // if the file was deleted in b, we don't want to copy this delta. - if (b.rearrangement.has_deleted_file(path_in_anc) - && !(a.rearrangement.has_added_file(path_in_merged))) + if (paths.right == file_path() && paths.merged == file_path()) { L(F("skipping delta '%s'->'%s' on deleted file '%s'\n") - % delta_entry_src(i) % delta_entry_dst(i) % path_in_anc); + % delta_entry_src(i) % delta_entry_dst(i) % paths.anc); continue; } @@ -2137,9 +2139,11 @@ { // if no deltas in b, copy ours over using the merged name L(F("merge is copying delta '%s' : '%s' -> '%s'\n") - % path_in_merged % delta_entry_src(i) % delta_entry_dst(i)); - I(b.deltas.find(path_in_merged) == b.deltas.end()); - b_merged.apply_delta(path_in_merged, delta_entry_src(i), delta_entry_dst(i)); + % paths.merged % delta_entry_src(i) % delta_entry_dst(i)); + I(b.deltas.find(paths.merged) == b.deltas.end()); + b_merged.apply_delta(paths.merged, + delta_entry_src(i), + delta_entry_dst(i)); } else { @@ -2161,10 +2165,12 @@ // 'a' change_set included a delta []->[...], ie file added. We want to // follow this fork so it gets added to the b_merged changeset L(F("propagating new file addition delta on '%s' : '%s' -> '%s'\n") - % path_in_merged + % paths.merged % delta_entry_src(j) % delta_entry_dst(i)); - b_merged.apply_delta(path_in_merged, delta_entry_src(i), delta_entry_dst(i)); + b_merged.apply_delta(paths.merged, + delta_entry_src(i), + delta_entry_dst(i)); } else if (null_id(delta_entry_src(j))) { @@ -2173,7 +2179,7 @@ // to add it to the b_merged changeset, since any delta in 'a' will be // ignored (as 'b' includes deletions). L(F("skipping new file addition delta on '%s' : '' -> '%s'\n") - % path_in_merged + % paths.merged % delta_entry_dst(j)); } } @@ -2181,122 +2187,323 @@ { // ... absorb identical deltas L(F("skipping common delta '%s' : '%s' -> '%s'\n") - % path_in_merged % delta_entry_src(i) % delta_entry_dst(i)); + % paths.merged % delta_entry_src(i) % delta_entry_dst(i)); } else if (delta_entry_src(i) == delta_entry_dst(i)) { L(F("skipping neutral delta on '%s' : %s -> %s\n") - % delta_entry_path(i) - % delta_entry_src(i) - % delta_entry_dst(i)); + % paths.left + % delta_entry_src(i) + % delta_entry_dst(i)); } else if (delta_entry_src(j) == delta_entry_dst(j)) { L(F("propagating unperturbed delta on '%s' : '%s' -> '%s'\n") - % delta_entry_path(i) - % delta_entry_src(i) - % delta_entry_dst(i)); - b_merged.apply_delta(path_in_merged, delta_entry_dst(j), delta_entry_dst(i)); + % paths.left + % delta_entry_src(i) + % delta_entry_dst(i)); + b_merged.apply_delta(paths.merged, + delta_entry_dst(j), + delta_entry_dst(i)); } else { // ... or resolve conflict L(F("merging delta '%s' : '%s' -> '%s' vs. '%s'\n") - % path_in_merged % delta_entry_src(i) % delta_entry_dst(i) % delta_entry_dst(j)); + % paths.merged % delta_entry_src(i) + % delta_entry_dst(i) % delta_entry_dst(j)); file_id finalist; - merge_deltas(path_in_anc, - delta_entry_path(i), // left_path - delta_entry_path(j), // right_path - path_in_merged, + merge_deltas(paths, merge_finalists, delta_entry_src(i), // anc delta_entry_dst(i), // left delta_entry_dst(j), // right finalist, merger); L(F("resolved merge to '%s' : '%s' -> '%s'\n") - % path_in_merged % delta_entry_src(i) % finalist); + % paths.merged % delta_entry_src(i) % finalist); // if the conflict resolved to something other than the // existing post-state of b, add a new entry to the deltas of // b finishing the job. if (! (finalist == delta_entry_dst(j))) - b_merged.apply_delta(path_in_merged, delta_entry_dst(j), finalist); + b_merged.apply_delta(paths.merged, + delta_entry_dst(j), + finalist); } } } } +void +process_filetree_history(revision_id const & anc, + revision_id const & left, + revision_id const & right, + change_set::path_rearrangement + const & right_extra_changes, + std::vector & paths, + change_set::path_rearrangement & lm_re, + change_set::path_rearrangement & rm_re, + app_state & app) +{ + typedef std::multimap::iterator gi; + typedef std::map > >::iterator ai; + std::multimap graph, rgraph; + app.db.get_revision_ancestry(graph); + for (gi i = graph.begin(); i != graph.end(); ++i) + rgraph.insert(std::make_pair(i->second, i->first)); + // rev -> {# of parents remaining, children} + std::map > > about; + std::deque todo, roots; + todo.push_back(left); + todo.push_back(right); + todo.push_back(anc); + about.insert(make_pair(left, make_pair(0, std::set()))); + about.insert(make_pair(right, make_pair(0, std::set()))); + about.insert(make_pair(anc, make_pair(0, std::set()))); + while(todo.size()) + { + revision_id c(todo.back()); + todo.pop_back(); + gi pb = rgraph.lower_bound(c); + gi pe = rgraph.upper_bound(c); + int n = 0; + for (gi i = pb; i != pe; ++i) + { + if (null_id(i->second)) + continue; + std::set s; + s.insert(c); + std::pair r; + r = about.insert(make_pair(i->second, make_pair(0, s))); + if (r.second) + todo.push_back(i->second); + else + r.first->second.second.insert(c); + ++n; + } + ai me = about.find(c); + I(me != about.end()); + me->second.first = n; + if (n == 0) + roots.push_back(c); + } + std::map trees; + tree_state emptytree(tree_state::new_tree()); + while(roots.size()) + { + revision_set rs; + app.db.get_revision(roots.front(), rs); + std::vector treevec; + std::vector revec; + for (edge_map::const_iterator i = rs.edges.begin(); + i != rs.edges.end(); ++i) + { + tree_state from(emptytree); + if (edge_old_revision(i) == revision_id()) + from = emptytree; + else + { + std::map::iterator + j = trees.find(edge_old_revision(i)); + I(j != trees.end()); + from = j->second; + } + treevec.push_back(from); + revec.push_back(edge_changes(i).rearrangement); + } + tree_state newtree(tree_state::merge_with_rearrangement(treevec, revec, + roots.front().inner()())); + trees.insert(make_pair(roots.front(), newtree)); + + ai i = about.find(roots.front()); + I(i != about.end()); + std::set const & cs(i->second.second); + for (std::set::const_iterator j = cs.begin(); + j != cs.end(); j++) + { + ai k = about.find(*j); + I(k != about.end()); + if (--(k->second.first) == 0) + roots.push_back(*j); + } + roots.pop_front(); + } + + std::map::const_iterator + i = trees.find(anc), + j = trees.find(left), + k = trees.find(right); + I(i != trees.end()); + I(j != trees.end()); + I(k != trees.end()); + tree_state a = i->second; + tree_state l = j->second; + std::vector rp; + rp.push_back(k->second); + std::vector rr; + rr.push_back(right_extra_changes); + tree_state r = tree_state::merge_with_rearrangement(rp, rr, "xyzzy"); + std::vector conf(l.conflict(r)); + std::set res; + E(conf.size() == 0, F("Cannot handle merge conflicts over filenames yet.")); + std::vector rl; + rl.push_back(l); + rl.push_back(r); + tree_state m = tree_state::merge_with_resolution(rl, res, "abccb"); + + std::map ip; + std::vector > ps; + ps = a.current(); + for (std::vector >::const_iterator + i = ps.begin(); i != ps.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + r.first->second.anc = i->second; + } + ps = l.current(); + for (std::vector >::const_iterator + i = ps.begin(); i != ps.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + r.first->second.left = (*i).second; + } + ps = r.current(); + for (std::vector >::const_iterator + i = ps.begin(); i != ps.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + r.first->second.right = (*i).second; + } + ps = m.current(); + for (std::vector >::const_iterator + i = ps.begin(); i != ps.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + r.first->second.merged = (*i).second; + } + paths.clear(); + paths.reserve(ip.size()); + for (std::map::const_iterator i = ip.begin(); + i != ip.end(); ++i) + paths.push_back(i->second); + + l.get_changes_for_merge(m, lm_re); + r.get_changes_for_merge(m, rm_re); +} + void -merge_change_sets(change_set const & a, - change_set const & b, - change_set & a_merged, - change_set & b_merged, - merge_provider & merger, - app_state & app) +merge_revisions(revision_id const & anc, + revision_id const & a, + revision_id const & b, + change_set const & b_extra_changes, + change_set & a_merged, + change_set & b_merged, + merge_provider & merger, + app_state & app) { - MM(a); - MM(b); MM(a_merged); MM(b_merged); - a.check_sane(); - b.check_sane(); - L(F("merging change sets\n")); + L(F("merging revisions\n")); - tid_source ts; - path_analysis - a_analysis, b_analysis, - a_renumbered, b_renumbered, - a_merged_analysis, b_merged_analysis; - MM(a_analysis); - MM(b_analysis); - MM(a_renumbered); - MM(b_renumbered); - MM(a_merged_analysis); - MM(b_merged_analysis); + std::vector paths; - analyze_rearrangement(a.rearrangement, a_analysis, ts); - analyze_rearrangement(b.rearrangement, b_analysis, ts); + std::map merge_finalists; - merge_disjoint_analyses(a_analysis, b_analysis, - a_renumbered, b_renumbered, - a_merged_analysis, b_merged_analysis, - ts, app); + change_set anc_a, anc_b, anc_bwithchanges; + MM(anc_a); + MM(anc_b); + MM(b_extra_changes); + MM(anc_bwithchanges); + if (null_id(anc)) + { + manifest_map a_man, b_man; + revision_set a_rev, b_rev; + MM(a_man); + MM(b_man); + app.db.get_revision(a, a_rev); + app.db.get_revision(b, b_rev); + app.db.get_manifest(a_rev.new_manifest, a_man); + app.db.get_manifest(b_rev.new_manifest, b_man); + build_pure_addition_change_set(a_man, anc_a); + build_pure_addition_change_set(b_man, anc_b); - compose_rearrangement(a_merged_analysis, - a_merged.rearrangement); + for (std::set::const_iterator + i = anc_a.rearrangement.added_files.begin(); + i != anc_a.rearrangement.added_files.end(); ++i) + if (!anc_b.rearrangement.has_added_file(*i)) + { + b_merged.add_file(*i); + paths.push_back(itempaths(file_path(), *i, file_path(), *i)); + } + else + paths.push_back(itempaths(file_path(), *i, *i, *i)); - compose_rearrangement(b_merged_analysis, - b_merged.rearrangement); + for (std::set::const_iterator + i = anc_b.rearrangement.added_files.begin(); + i != anc_b.rearrangement.added_files.end(); ++i) + if (!anc_a.rearrangement.has_added_file(*i)) + { + a_merged.add_file(*i); + paths.push_back(itempaths(file_path(), file_path(), *i, *i)); + } + } + else + { + process_filetree_history(anc, a, b, b_extra_changes.rearrangement, + paths, + a_merged.rearrangement, + b_merged.rearrangement, + app); + if (!(anc == a)) + calculate_arbitrary_change_set(anc, a, app, anc_a); + if (!(anc == b)) + calculate_arbitrary_change_set(anc, b, app, anc_b); + } + concatenate_change_sets(anc_b, b_extra_changes, anc_bwithchanges); - std::map merge_finalists; - - project_missing_deltas(a, b, - a_renumbered, b_renumbered, - a_merged_analysis, + project_missing_deltas(anc_a, anc_bwithchanges, + paths, b_merged, merger, merge_finalists); - project_missing_deltas(b, a, - b_renumbered, a_renumbered, - b_merged_analysis, + b_merged.check_sane(); + + for (std::vector::iterator i = paths.begin(); + i != paths.end(); ++i) + { + file_path t = (*i).left; + (*i).left = (*i).right; + (*i).right = t; + } + + project_missing_deltas(anc_bwithchanges, anc_a, + paths, a_merged, merger, merge_finalists); + a_merged.check_sane(); + { // confirmation step change_set a_check, b_check; + MM(a_check); + MM(b_check); // dump_change_set("a", a); // dump_change_set("a_merged", a_merged); // dump_change_set("b", b); // dump_change_set("b_merged", b_merged); - concatenate_change_sets(a, a_merged, a_check); - concatenate_change_sets(b, b_merged, b_check); + concatenate_change_sets(anc_a, a_merged, a_check); + concatenate_change_sets(anc_bwithchanges, b_merged, b_check); // dump_change_set("a_check", a_check); // dump_change_set("b_check", b_check); I(a_check == b_check); @@ -3005,77 +3212,6 @@ } } -static void -disjoint_merge_test(std::string const & ab_str, - std::string const & ac_str) -{ - change_set ab, ac, bm, cm; - - app_state app; - - L(F("beginning disjoint_merge_test\n")); - - read_change_set(data(ab_str), ab); - read_change_set(data(ac_str), ac); - - manifest_map dummy; - - merge_provider merger(app, dummy, dummy, dummy); - merge_change_sets(ab, ac, bm, cm, merger, app); - - dump_change_set("ab", ab); - dump_change_set("ac", ac); - dump_change_set("bm", bm); - dump_change_set("cm", cm); - - BOOST_CHECK(bm.rearrangement == ac.rearrangement); - BOOST_CHECK(cm.rearrangement == ab.rearrangement); - - L(F("finished disjoint_merge_test\n")); -} - -static void -disjoint_merge_tests() -{ - disjoint_merge_test - ("rename_file \"foo\"\n" - " to \"bar\"\n", - - "rename_file \"apple\"\n" - " to \"orange\"\n"); - - disjoint_merge_test - ("rename_file \"foo/a.txt\"\n" - " to \"bar/b.txt\"\n", - - "rename_file \"bar/c.txt\"\n" - " to \"baz/d.txt\"\n"); - - disjoint_merge_test - ("patch \"foo/file.txt\"\n" - " from [c6a4a6196bb4a744207e1a6e90273369b8c2e925]\n" - " to [fe18ec0c55cbc72e4e51c58dc13af515a2f3a892]\n", - - "rename_file \"foo/file.txt\"\n" - " to \"foo/apple.txt\"\n"); - - disjoint_merge_test - ( - "rename_file \"apple.txt\"\n" - " to \"pear.txt\"\n" - "\n" - "patch \"foo.txt\"\n" - " from [c6a4a6196bb4a744207e1a6e90273369b8c2e925]\n" - " to [fe18ec0c55cbc72e4e51c58dc13af515a2f3a892]\n", - - "rename_file \"foo.txt\"\n" - " to \"bar.txt\"\n" - "\n" - "patch \"apple.txt\"\n" - " from [fe18ec0c55cbc72e4e51c58dc13af515a2f3a892]\n" - " to [435e816c30263c9184f94e7c4d5aec78ea7c028a]\n"); -} - static void basic_change_set_test() { @@ -3645,7 +3781,6 @@ suite->add(BOOST_TEST_CASE(&basic_change_set_test)); suite->add(BOOST_TEST_CASE(&neutralize_change_test)); suite->add(BOOST_TEST_CASE(&non_interfering_change_test)); - suite->add(BOOST_TEST_CASE(&disjoint_merge_tests)); suite->add(BOOST_TEST_CASE(&bad_concatenate_change_tests)); suite->add(BOOST_TEST_CASE(&invert_change_test)); } ======================================================================== --- change_set.hh d530354cb57477ef5e3b9e961346861c61508cba +++ change_set.hh f8e06d50c8b82b973b2de70137f70171dac6120d @@ -138,6 +138,7 @@ struct merge_provider; +/* void merge_change_sets(change_set const & a, change_set const & b, @@ -145,6 +146,16 @@ change_set & b_merged, merge_provider & merger, app_state & app); +*/ +void +merge_revisions(revision_id const & anc, + revision_id const & a, + revision_id const & b, + change_set const & b_extra_changes, + change_set & a_merged, + change_set & b_merged, + merge_provider & merger, + app_state & app); // value-oriented access to printers and parsers ======================================================================== --- commands.cc e3432b9b21954ad6f3628a6afbff3e6328e3cea1 +++ commands.cc 5c0ba1f50fee0b05d49e0f705875b563d03efcfd @@ -3043,11 +3043,11 @@ // we apply the working to merged changeset to the working copy // and keep the rearrangement from chosen to merged changeset in MT/work - merge_change_sets(old_to_chosen, - old_to_working, - chosen_to_merged, - working_to_merged, - merger, app); + merge_revisions(r_old_id, r_chosen_id, r_old_id, + old_to_working, + chosen_to_merged, + working_to_merged, + merger, app); // dump_change_set("chosen to merged", chosen_to_merged); // dump_change_set("working to merged", working_to_merged); @@ -3151,16 +3151,16 @@ } else { - P(F("no common ancestor found, synthesizing edges\n")); + P(F("no common ancestor found, synthesizing edges\n")); build_pure_addition_change_set(left_man, *anc_to_left); build_pure_addition_change_set(right_man, *anc_to_right); } merge_provider merger(app, anc_man, left_man, right_man); - merge_change_sets(*anc_to_left, *anc_to_right, - *left_to_merged, *right_to_merged, - merger, app); + merge_revisions(anc_id, left_id, right_id, change_set(), + *left_to_merged, *right_to_merged, + merger, app); { // we have to record *some* route to this manifest. we pick the ======================================================================== --- pcdv.cc 98142af12f61b816c38685f185fe3eaae0447dca +++ pcdv.cc 7bb131b0c0268cae51eadc9d22a11d7ec6d7ad11 @@ -974,24 +974,58 @@ } - -tree_state::tree_state(boost::shared_ptr > > _items, - boost::shared_ptr > _itx): - items(_items), - states(new std::map()), - itx(_itx) -{} - tree_state::tree_state(): items(new vector >()), states(new std::map()), - itx(new interner()) + itx(new interner()), + sutures(new std::map()) {} +tree_state +tree_state::new_skel() const +{ + tree_state out; + out.items = items; + out.itx = itx; + out.sutures = sutures; + return out; +} + tree_state::~tree_state() {} +void +tree_state::add_suture(item_id l, item_id r) +{ + std::map::const_iterator i = sutures->find(r); + if (i != sutures->end()) + add_suture(l, i->second); +} + +void +tree_state::apply_sutures() +{ + for (std::map::const_iterator i = sutures->begin(); + i != sutures->end(); ++i) + { + std::map::iterator j = states->find(i->first); + std::map::iterator k = states->find(i->second); + if (j != states->end()) + { + if (k == states->end()) + { + states->insert(make_pair(i->second, j->second)); + states->erase(j); + } + else + { + k->second = k->second.suture(j->second); + states->erase(j); + } + } + } +} + const int deleted_dir(1); const int deleted_file(2); const int renamed_dir(3); @@ -1156,13 +1190,7 @@ if (r.first->second != j->first) { W(F("Colliding over %1%") % j->second()); - std::map::iterator a, b; - a = out.states->find(r.first->second); - b = out.states->find(j->first); - I(a != out.states->end()); - I(b != out.states->end()); - a->second = a->second.suture(b->second); - out.states->erase(b); + out.add_suture(r.first->second, j->first); } } } @@ -1266,15 +1294,10 @@ if (r.first->second != current_id) { W(F("Colliding over %1%") % to); - std::map::iterator a, b; - a = out.states->find(r.first->second); - b = out.states->find(j->first); - I(a != out.states->end()); - I(b != out.states->end()); - a->second = a->second.suture(b->second); - out.states->erase(b); + out.add_suture(r.first->second, j->first); } } + out.apply_sutures(); return out; } @@ -1296,7 +1319,7 @@ tree_state::mash(tree_state const & other) const { I(items == other.items); - tree_state newstate(items, itx); + tree_state newstate = new_skel(); map::const_iterator l, r; l = states->begin(); r = other.states->begin(); @@ -1324,6 +1347,7 @@ tree_state::conflict(tree_state const & other) const { tree_state merged(mash(other)); + merged.apply_sutures(); std::vector out; std::map > m; @@ -1510,6 +1534,7 @@ std::string const & revision) { tree_state merged(mash(revs)); + merged.apply_sutures(); std::set resolved; typedef std::vector splitpath; ======================================================================== --- pcdv.hh f650089a46f20388d4d5976d906389af52c23934 +++ pcdv.hh 42ce2c9da4c7a1e77b1200885b6d3fc579592c69 @@ -227,7 +227,7 @@ }; -// This is a const object type; there are no modifiers. +// This is a const object type; there are no modifiers. ?? // Usage: // for a->b // a.rearrange(, 'b') @@ -247,11 +247,14 @@ boost::shared_ptr > > items; boost::shared_ptr > states; boost::shared_ptr > itx; + boost::shared_ptr > sutures; - tree_state(boost::shared_ptr > > _items, - boost::shared_ptr > _itx); tree_state(); + + tree_state new_skel() const; + + void add_suture(item_id l, item_id r); + void apply_sutures(); public: ~tree_state();