# # patch "ChangeLog" # from [36f603b18daca0b7448f42dd1fde6fcaaf3e5e3e] # to [9641d149ddeca37d295871949c85b7e57c708bf0] # # patch "change_set.cc" # from [2d1bd3032cf730ae58836127e0b11a0719acba10] # to [85337f4c912245cda1d3688df7a95ee05dd43f29] # # patch "change_set.hh" # from [f8e06d50c8b82b973b2de70137f70171dac6120d] # to [07b4b1de8d6ab268425d121270a8f08f38fa29ca] # # patch "commands.cc" # from [361d950489326b0dca8e1e94ca9c62e824bd0050] # to [b5da86e6f37b0e8e2d5562077d0dc93561c2318b] # ======================================================================== --- ChangeLog 36f603b18daca0b7448f42dd1fde6fcaaf3e5e3e +++ ChangeLog 9641d149ddeca37d295871949c85b7e57c708bf0 @@ -1,5 +1,12 @@ 2005-08-21 Timothy Brownawell + * change_set.{cc,hh}: New function, transplant_change_set . Used + for update. + * change_set.cc: use versioned scalars for tracking file hashes. + Clean up project_missing_deltas. + +2005-08-21 Timothy Brownawell + Fix bad merge (user error). 2005-08-20 Timothy Brownawell ======================================================================== --- change_set.cc 2d1bd3032cf730ae58836127e0b11a0719acba10 +++ change_set.cc 85337f4c912245cda1d3688df7a95ee05dd43f29 @@ -1654,12 +1654,17 @@ file_path left; file_path right; file_path merged; + file_id ahash; + file_id lhash; + file_id rhash; + file_id mhash; bool clean; - file_id hash; - 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(file_path const & l, file_path const & r, file_path const & m, + file_id const & lh, file_id const & rh, file_id const & mh): + anc(file_path()), left(l), right(r), merged(m), + ahash(file_id()), lhash(lh), rhash(rh), mhash(mh), + clean(!(mh == file_id())) {} itempaths() {} @@ -1693,9 +1698,6 @@ static void merge_deltas(itempaths const & paths, std::map & merge_finalists, - file_id const & anc, - file_id const & left, - file_id const & right, file_id & finalist, merge_provider & merger) { @@ -1704,186 +1706,229 @@ if (i != merge_finalists.end()) { L(F("reusing merge resolution '%s' : '%s' -> '%s'\n") - % paths.merged % anc % i->second); + % paths.merged % paths.ahash % i->second); finalist = i->second; } else { - if (null_id(anc)) + if (null_id(paths.ahash)) { N(merger.try_to_merge_files(paths.left, paths.right, paths.merged, - left, right, finalist), + paths.lhash, paths.rhash, finalist), F("merge of '%s' : '%s' vs. '%s' (no common ancestor) failed") - % paths.merged % left % right); + % paths.merged % paths.lhash % paths.rhash); } else { N(merger.try_to_merge_files(paths.anc, paths.left, paths.right, paths.merged, - anc, left, right, finalist), + paths.ahash, paths.lhash, paths.rhash, + finalist), F("merge of '%s' : '%s' -> '%s' vs '%s' failed") - % paths.merged % anc % left % right); + % paths.merged % paths.ahash % paths.lhash % paths.rhash); } L(F("merge of '%s' : '%s' -> '%s' vs '%s' resolved to '%s'\n") - % paths.merged % anc % left % right % finalist); + % paths.merged % paths.ahash % paths.lhash % paths.rhash % finalist); merge_finalists.insert(std::make_pair(paths.merged, finalist)); } } static void -project_missing_deltas(change_set const & a, - change_set const & b, - std::vector const & pathset, - change_set & b_merged, +project_missing_deltas(std::vector const & pathset, + change_set & l_merged, + change_set & r_merged, merge_provider & merger, std::map & merge_finalists) { - std::map pathsmap; +// b_merged needs deltas for +// merged != file_path() && rhash != mhash +// if !clean, then run the merger 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(); - i != a.deltas.end(); ++i) { - // we have a fork like this: - // - // - // +--> [a2] - // [a1==b1] - // +--> [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. - - std::map::const_iterator - x = pathsmap.find(delta_entry_path(i)); - I(x != pathsmap.end()); - itempaths const & paths = x->second; - - // 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(paths.right); - - // if the file was deleted in b, we don't want to copy this delta. - if (paths.right == file_path() && paths.merged == file_path()) + itempaths const & paths(*i); + if (paths.merged == file_path()) + continue; + if (paths.clean) { - L(F("skipping delta '%s'->'%s' on deleted file '%s'\n") - % delta_entry_src(i) % delta_entry_dst(i) % paths.anc); - continue; + L(F("File '%s' clean merged to '%s' by hash") + % paths.merged % paths.mhash); + if (!(paths.lhash == paths.mhash)) + l_merged.apply_delta(paths.merged, paths.lhash, paths.mhash); + if (!(paths.rhash == paths.mhash)) + r_merged.apply_delta(paths.merged, paths.rhash, paths.mhash); } - - if (j == b.deltas.end()) + else { - // if no deltas in b, copy ours over using the merged name - L(F("merge is copying delta '%s' : '%s' -> '%s'\n") - % paths.merged % delta_entry_src(i) % delta_entry_dst(i)); - I(b.deltas.find(paths.merged) == b.deltas.end() - || a.rearrangement.has_deleted_file(paths.merged)); - b_merged.apply_delta(paths.merged, - delta_entry_src(i), - delta_entry_dst(i)); + file_id finalist; + merge_deltas(paths, + merge_finalists, + finalist, merger); + L(F("resolved merge to '%s' : '%s'\n") + % paths.merged % finalist); + + if (!(finalist == paths.lhash)) + l_merged.apply_delta(paths.merged, + paths.lhash, + finalist); + if (!(finalist == paths.rhash)) + r_merged.apply_delta(paths.merged, + paths.rhash, + finalist); } - else - { - // if so, either... + } +} - if (!(delta_entry_src(i) == delta_entry_src(j))) - { - // This is a bit of a corner case where a file was added then deleted on one - // of the forks. The src for the addition fork will be null_id, but the src - // for the other fork will be the ancestor file's id. +void +calculate_itempaths(tree_state const & a, + tree_state const & l, + tree_state const & r, + tree_state const & m, + std::vector & paths, + interner & itx) +{ + std::map ip; + std::vector > ps; + std::map > sm; + 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; + } - // if neither of the forks involved a file addition delta (null_id to something) - // then something bad happened. - I(null_id(delta_entry_src(i)) || null_id(delta_entry_src(j))); - if (null_id(delta_entry_src(i))) - { - // ... use the delta from 'a' - // '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") - % paths.merged - % delta_entry_src(j) - % 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))) - { - // ... ignore the delta - // 'b' change_set included a delta []->[...], ie file added. We don't need - // 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") - % paths.merged - % delta_entry_dst(j)); - } - } - else if (delta_entry_dst(i) == delta_entry_dst(j)) - { - // ... absorb identical deltas - L(F("skipping common delta '%s' : '%s' -> '%s'\n") - % paths.merged % delta_entry_src(i) % delta_entry_dst(i)); - } + sm = a.current_scalars(); + for (std::map >::const_iterator + i = sm.begin(); i != sm.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + I((*i).second.size() == 1); + r.first->second.ahash = file_id(itx.lookup(*(*i).second.begin())); + } - else if (delta_entry_src(i) == delta_entry_dst(i)) - { - L(F("skipping neutral delta on '%s' : %s -> %s\n") - % paths.left - % delta_entry_src(i) - % delta_entry_dst(i)); - } + sm = l.current_scalars(); + for (std::map >::const_iterator + i = sm.begin(); i != sm.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + I((*i).second.size() == 1); + r.first->second.lhash = file_id(itx.lookup(*(*i).second.begin())); + } - else if (delta_entry_src(j) == delta_entry_dst(j)) - { - L(F("propagating unperturbed delta on '%s' : '%s' -> '%s'\n") - % paths.left - % delta_entry_src(i) - % delta_entry_dst(i)); - b_merged.apply_delta(paths.merged, - delta_entry_dst(j), - delta_entry_dst(i)); - } + sm = r.current_scalars(); + for (std::map >::const_iterator + i = sm.begin(); i != sm.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + I((*i).second.size() == 1); + r.first->second.rhash = file_id(itx.lookup(*(*i).second.begin())); + } - else - { - // ... or resolve conflict - L(F("merging delta '%s' : '%s' -> '%s' vs. '%s'\n") - % paths.merged % delta_entry_src(i) - % delta_entry_dst(i) % delta_entry_dst(j)); - file_id finalist; + sm = m.current_scalars(); + for (std::map >::const_iterator + i = sm.begin(); i != sm.end(); ++i) + { + std::pair::iterator, bool> r; + r = ip.insert(std::make_pair(i->first, itempaths())); + if ((*i).second.size() == 1) + { + r.first->second.clean = true; + r.first->second.mhash = file_id(itx.lookup(*(*i).second.begin())); + } + else + r.first->second.clean = false; + } - 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") - % paths.merged % delta_entry_src(i) % finalist); + paths.clear(); + paths.reserve(ip.size()); + for (std::map::const_iterator i = ip.begin(); + i != ip.end(); ++i) + paths.push_back(i->second); +} - // 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(paths.merged, - delta_entry_dst(j), - finalist); - } +tree_state +merge_trees(std::vector const & treevec, + std::vector const & chvec, + interner & itx, + std::string revision) +{ + std::vector revec; + std::map sc; + for (std::vector::const_iterator i = chvec.begin(); + i != chvec.end(); ++i) + { + revec.push_back(i->rearrangement); + for (change_set::delta_map::const_iterator + j = i->deltas.begin(); + j != i->deltas.end(); ++j) + { + sc.insert(make_pair(delta_entry_path(j), + itx.intern(delta_entry_dst(j).inner()()))); } } + tree_state newtree(tree_state::merge_with_rearrangement(treevec, revec, + revision)); + return newtree.set_scalars(revision, sc); } +tree_state +merge_trees(tree_state l, tree_state r) +{ + std::vector conf(l.conflict(r)); + MM(conf); + std::set res; + for (std::vector::const_iterator i = conf.begin(); + i != conf.end(); ++i) + { + E(i->type != path_conflict::split, + F("Cannot handle filename conflicts yet.")); + if (i->type == path_conflict::collision) + W(F("Filename collision, suturing...")); + } + std::vector lr; + lr.push_back(l); + lr.push_back(r); + tree_state m = tree_state::merge_with_resolution(lr, res, "abccb"); + N(m.conflict(m).empty(), F("Provided filename resolution is inconsistent.")); + return m; +} + 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, @@ -1942,8 +1987,7 @@ revision_set rs; app.db.get_revision(roots.front(), rs); std::vector treevec; - std::vector revec; - MM(revec); + std::vector chvec; std::map sc; for (edge_map::const_iterator i = rs.edges.begin(); i != rs.edges.end(); ++i) @@ -1959,21 +2003,12 @@ from = j->second; } treevec.push_back(from); - revec.push_back(edge_changes(i).rearrangement); - for (change_set::delta_map::const_iterator - j = edge_changes(i).deltas.begin(); - j != edge_changes(i).deltas.end(); ++j) - { - sc.insert(make_pair(delta_entry_path(j), - itx.intern(delta_entry_dst(j).inner()()))); - } + chvec.push_back(edge_changes(i)); } - tree_state newtree1(tree_state::merge_with_rearrangement(treevec, revec, - roots.front().inner()())); - tree_state newtree2(newtree1.set_scalars(roots.front().inner()(), sc)); + trees.insert(make_pair(roots.front(), + merge_trees(treevec, chvec, itx, + roots.front().inner()()))); - trees.insert(make_pair(roots.front(), newtree2)); - ai i = about.find(roots.front()); I(i != about.end()); std::set const & cs(i->second.second); @@ -1998,96 +2033,52 @@ 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"); + tree_state r = k->second; // do the merge - std::vector conf(l.conflict(r)); - MM(conf); - std::set res; - for (std::vector::const_iterator i = conf.begin(); - i != conf.end(); ++i) - { - E(i->type != path_conflict::split, - F("Cannot handle filename conflicts yet.")); - if (i->type == path_conflict::collision) - W(F("Filename collision, suturing...")); - } - std::vector rl; - rl.push_back(l); - rl.push_back(r); - tree_state m = tree_state::merge_with_resolution(rl, res, "abccb"); - N(m.conflict(m).empty(), F("Provided filename resolution is inconsistent.")); + tree_state m = merge_trees(l, r); // calculate outputs - 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; - } + calculate_itempaths(a, l, r, m, paths, itx); + l.get_changes_for_merge(m, lm_re); + r.get_changes_for_merge(m, rm_re); +} - std::map > sm = m.current_scalars(); - for (std::map >::const_iterator - i = sm.begin(); i != sm.end(); ++i) - { - std::pair::iterator, bool> r; - r = ip.insert(std::make_pair(i->first, itempaths())); - if ((*i).second.size() == 1) - { - r.first->second.clean = true; - r.first->second.hash = file_id(itx.lookup(*(*i).second.begin())); - } - else - r.first->second.clean = false; - } +void +check_merge(change_set const & anc_a, change_set & a_merged, + change_set const & anc_b, change_set & b_merged) +{ + L(F("Checking merge...")); + a_merged.check_sane(); + b_merged.check_sane(); - paths.clear(); - paths.reserve(ip.size()); - for (std::map::const_iterator i = ip.begin(); - i != ip.end(); ++i) - paths.push_back(i->second); + { + // 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(anc_a, a_merged, a_check); + concatenate_change_sets(anc_b, b_merged, b_check); + // dump_change_set("a_check", a_check); + // dump_change_set("b_check", b_check); + I(a_check == b_check); + } - l.get_changes_for_merge(m, lm_re); - r.get_changes_for_merge(m, rm_re); + normalize_change_set(a_merged); + normalize_change_set(b_merged); + + a_merged.check_sane(); + b_merged.check_sane(); } 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, @@ -2103,8 +2094,6 @@ 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; @@ -2121,27 +2110,43 @@ 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)); + { + manifest_map::const_iterator j = a_man.find(*i); + I(j != a_man.end()); + file_id a_id = manifest_entry_id(j); + if (!anc_b.rearrangement.has_added_file(*i)) + { + b_merged.add_file(*i); + paths.push_back(itempaths(*i, file_path(), *i, + a_id, file_id(), a_id)); + } + else + { + manifest_map::const_iterator k = b_man.find(*i); + I(k != b_man.end()); + file_id b_id = manifest_entry_id(k); + file_id m_id = ((a_id == b_id)?a_id:file_id()); + paths.push_back(itempaths(*i, *i, *i, + a_id, b_id, m_id)); + } + } 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)) { + manifest_map::const_iterator k = b_man.find(*i); + I(k != b_man.end()); + file_id b_id = manifest_entry_id(k); a_merged.add_file(*i); - paths.push_back(itempaths(file_path(), file_path(), *i, *i)); + paths.push_back(itempaths(file_path(), *i, *i, + file_id(), b_id, b_id)); } } else { - process_filetree_history(anc, a, b, b_extra_changes.rearrangement, - paths, + process_filetree_history(anc, a, b, paths, a_merged.rearrangement, b_merged.rearrangement, app); @@ -2150,55 +2155,61 @@ if (!(anc == b)) calculate_arbitrary_change_set(anc, b, app, anc_b); } - concatenate_change_sets(anc_b, b_extra_changes, anc_bwithchanges); + MM(a_merged); MM(b_merged); - project_missing_deltas(anc_a, anc_bwithchanges, - paths, - b_merged, + project_missing_deltas(paths, a_merged, b_merged, merger, merge_finalists); - for (std::vector::iterator i = paths.begin(); - i != paths.end(); ++i) - { - file_path t = (*i).left; - (*i).left = (*i).right; - (*i).right = t; - } + check_merge(anc_a, a_merged, anc_b, b_merged); + L(F("finished merge\n")); +} - MM(a_merged); - project_missing_deltas(anc_bwithchanges, anc_a, - paths, - a_merged, - merger, merge_finalists); +void +transplant_change_set(revision_id const & from, + revision_id const & to, + change_set const & cs, + change_set & to_res, + change_set & cs_res, + merge_provider & merger, + app_state & app) +{ + manifest_map from_man; + revision_set from_rev; + change_set from_cs, from_to_cs; - L(F("Checking merge...")); - a_merged.check_sane(); - b_merged.check_sane(); + app.db.get_revision(from, from_rev); + app.db.get_manifest(from_rev.new_manifest, from_man); + build_pure_addition_change_set(from_man, from_cs); - { - // 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(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); - } + calculate_arbitrary_change_set(from, to, app, from_to_cs); - normalize_change_set(a_merged); - normalize_change_set(b_merged); + tree_state emptytree(tree_state::new_tree()); + interner itx; + std::vector treevec; + std::vector chvec; + treevec.push_back(emptytree); + chvec.push_back(from_cs); + tree_state anc = merge_trees(treevec, chvec, itx, "from"); + idx(treevec, 0) = anc; + idx(chvec, 0) = from_to_cs; + tree_state left = merge_trees(treevec, chvec, itx, "to"); +// idx(treevec, 0) = from; + idx(chvec, 0) = cs; + tree_state changes = merge_trees(treevec, chvec, itx, "changes"); - a_merged.check_sane(); - b_merged.check_sane(); + // merge + tree_state result = merge_trees(left, changes); - L(F("finished merge\n")); + // calculate outputs + std::vector paths; + calculate_itempaths(anc, left, changes, result, paths, itx); + left.get_changes_for_merge(result, to_res.rearrangement); + changes.get_changes_for_merge(result, cs_res.rearrangement); + std::map merge_finalists; + project_missing_deltas(paths, to_res, cs_res, + merger, merge_finalists); + check_merge(from_to_cs, to_res, cs, cs_res); } // end stuff related to merging ======================================================================== --- change_set.hh f8e06d50c8b82b973b2de70137f70171dac6120d +++ change_set.hh 07b4b1de8d6ab268425d121270a8f08f38fa29ca @@ -138,25 +138,25 @@ struct merge_provider; -/* + 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); -*/ -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); +void +transplant_change_set(revision_id const & from, + revision_id const & to, + change_set const & cs, + change_set & to_res, + change_set & cs_res, + merge_provider & merger, + app_state & app); + // value-oriented access to printers and parsers void ======================================================================== --- commands.cc 361d950489326b0dca8e1e94ca9c62e824bd0050 +++ commands.cc b5da86e6f37b0e8e2d5562077d0dc93561c2318b @@ -2897,11 +2897,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_revisions(r_old_id, r_chosen_id, r_old_id, - old_to_working, - chosen_to_merged, - working_to_merged, - merger, app); + transplant_change_set(r_old_id, r_chosen_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); @@ -3012,7 +3012,7 @@ merge_provider merger(app, anc_man, left_man, right_man); - merge_revisions(anc_id, left_id, right_id, change_set(), + merge_revisions(anc_id, left_id, right_id, *left_to_merged, *right_to_merged, merger, app);