# # patch "ChangeLog" # from [1486b1d2adf0958d9f9b7f79e1a6083d49d852da] # to [55e1dd795c1b8f0884719a7aebd5cf2766b04e53] # # patch "change_set.cc" # from [dca63f6f968128d67da90a44a1c2bb09e0d9c0ed] # to [d653b309754b89d55c8c8801122d282e363f2d7f] # # patch "pcdv.cc" # from [7bb131b0c0268cae51eadc9d22a11d7ec6d7ad11] # to [fb89aec9ecf3531efeea6d7f84d8045e24152b59] # # patch "pcdv.hh" # from [42ce2c9da4c7a1e77b1200885b6d3fc579592c69] # to [7ba58f71a35e6f8e212bc926d0ec2f15d00c2dd6] # # patch "sanity.cc" # from [36ad241e17f12b3d4e08f78ba1f302bb76523a76] # to [ffb40b74ba38772ebf90bf7bc0f7ac710933a338] # # patch "tests/t_merge_add_del.at" # from [20f850e8ede0f7c4240c1f9cb21855f151f89df2] # to [7abec2ef42ef3e1ae12434eb8fea72855ae3781e] # # patch "tests/t_rename_added_in_rename.at" # from [986d6f8c484b4cd73df3e6df7c67ad61f3bd1a2d] # to [c73d2f1b0a4ba14c9165e2775c5e995a656c951d] # ======================================================================== --- ChangeLog 1486b1d2adf0958d9f9b7f79e1a6083d49d852da +++ ChangeLog 55e1dd795c1b8f0884719a7aebd5cf2766b04e53 @@ -1,3 +1,18 @@ +2005-08-17 Timothy Brownawell + + Improve testsuite compliance: 1 failure remaining. + * tests/t_merge_add_del.at: Remove XFAIL + * pcdv.{cc,hh}, change_set.cc: (Ick! Ick! Ick!) allow tree + merger to generate sutures. Bugfix suture handling code. + Bugfix tree_state::get_changes_for_merge . + * sanity.cc (gasp()): When catching an error from dumping a MM'd + variable, do not discard output generated prior to the error. This + way, at least the header line (function name, file, line no.) is + printed. + * change_set.cc: write_insane_change_set: new function to write a + change set without sanity checking it, now used by dump(). + * tests/t_rename_added_in_rename.at: Make comment match the code. + 2005-08-16 Timothy Brownawell * change_set.{cc,hh}: Replace tree merger with the one in pcdv.{cc,hh}. ======================================================================== --- change_set.cc dca63f6f968128d67da90a44a1c2bb09e0d9c0ed +++ change_set.cc d653b309754b89d55c8c8801122d282e363f2d7f @@ -2054,6 +2054,31 @@ {} }; +void dump(itempaths const & obj, std::string & out) +{ + std::string o; + dump(obj.anc, o); + out = "Ancestor: " + o; + dump(obj.left, o); + out += "\nLeft: " + o; + dump(obj.right, o); + out += "\nRight: " + o; + dump(obj.merged, o); + out += "\nMerged: " + o; +} + +void dump(std::vector const & obj, std::string & out) +{ + out.clear(); + std::string o; + for (std::vector::const_iterator i = obj.begin(); + i != obj.end(); ++i) + { + dump(*i, o); + out += o + "\n"; + } +} + static void merge_deltas(itempaths const & paths, std::map & merge_finalists, @@ -2122,7 +2147,10 @@ // 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. - itempaths const & paths = pathsmap[delta_entry_path(i)]; + 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); @@ -2140,7 +2168,8 @@ // 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()); + 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)); @@ -2251,6 +2280,8 @@ { typedef std::multimap::iterator gi; typedef std::map > >::iterator ai; + + // process history std::multimap graph, rgraph; app.db.get_revision_ancestry(graph); for (gi i = graph.begin(); i != graph.end(); ++i) @@ -2334,6 +2365,7 @@ roots.pop_front(); } + // find the interesting revisions std::map::const_iterator i = trees.find(anc), j = trees.find(left), @@ -2348,14 +2380,25 @@ std::vector rr; rr.push_back(right_extra_changes); tree_state r = tree_state::merge_with_rearrangement(rp, rr, "xyzzy"); + + // do the merge std::vector conf(l.conflict(r)); std::set res; - E(conf.size() == 0, F("Cannot handle merge conflicts over filenames yet.")); + 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"); + I(m.conflict(m).empty()); + // calculate outputs std::map ip; std::vector > ps; ps = a.current(); @@ -2410,12 +2453,10 @@ merge_provider & merger, app_state & app) { - MM(a_merged); - MM(b_merged); - L(F("merging revisions\n")); std::vector paths; + MM(paths); std::map merge_finalists; @@ -2471,13 +2512,12 @@ } concatenate_change_sets(anc_b, b_extra_changes, anc_bwithchanges); + MM(b_merged); project_missing_deltas(anc_a, anc_bwithchanges, paths, b_merged, merger, merge_finalists); - b_merged.check_sane(); - for (std::vector::iterator i = paths.begin(); i != paths.end(); ++i) { @@ -2486,12 +2526,15 @@ (*i).right = t; } + MM(a_merged); project_missing_deltas(anc_bwithchanges, anc_a, paths, a_merged, merger, merge_finalists); + L(F("Checking merge...")); a_merged.check_sane(); + b_merged.check_sane(); { // confirmation step @@ -3024,11 +3067,10 @@ void -print_path_rearrangement(basic_io::printer & printer, - change_set::path_rearrangement const & pr) +print_insane_path_rearrangement(basic_io::printer & printer, + change_set::path_rearrangement const & pr) { - pr.check_sane(); for (std::set::const_iterator i = pr.deleted_files.begin(); i != pr.deleted_files.end(); ++i) { @@ -3073,6 +3115,14 @@ } void +print_path_rearrangement(basic_io::printer & printer, + change_set::path_rearrangement const & pr) +{ + pr.check_sane(); + print_insane_path_rearrangement(printer, pr); +} + +void parse_change_set(basic_io::parser & parser, change_set & cs) { @@ -3097,11 +3147,10 @@ } void -print_change_set(basic_io::printer & printer, - change_set const & cs) +print_insane_change_set(basic_io::printer & printer, + change_set const & cs) { - cs.check_sane(); - print_path_rearrangement(printer, cs.rearrangement); + print_insane_path_rearrangement(printer, cs.rearrangement); for (change_set::delta_map::const_iterator i = cs.deltas.begin(); i != cs.deltas.end(); ++i) @@ -3114,6 +3163,14 @@ } } +void +print_change_set(basic_io::printer & printer, + change_set const & cs) +{ + cs.check_sane(); + print_insane_change_set(printer, cs); +} + void read_path_rearrangement(data const & dat, change_set::path_rearrangement & re) @@ -3143,17 +3200,24 @@ } void -write_change_set(change_set const & cs, - data & dat) +write_insane_change_set(change_set const & cs, + data & dat) { - cs.check_sane(); std::ostringstream oss; basic_io::printer pr(oss); - print_change_set(pr, cs); + print_insane_change_set(pr, cs); dat = data(oss.str()); } void +write_change_set(change_set const & cs, + data & dat) +{ + cs.check_sane(); + write_insane_change_set(cs, dat); +} + +void write_path_rearrangement(change_set::path_rearrangement const & re, data & dat) { @@ -3168,7 +3232,8 @@ dump(change_set const & cs, std::string & out) { data tmp; - write_change_set(cs, tmp); + write_insane_change_set(cs, tmp); +// write_change_set(cs, tmp); out = tmp(); } ======================================================================== --- pcdv.cc 7bb131b0c0268cae51eadc9d22a11d7ec6d7ad11 +++ pcdv.cc fb89aec9ecf3531efeea6d7f84d8045e24152b59 @@ -997,13 +997,22 @@ void tree_state::add_suture(item_id l, item_id r) { + I(l != r); + if (l < r) + { + item_id t = r; + r = l; + l = t; + } std::map::const_iterator i = sutures->find(r); if (i != sutures->end()) add_suture(l, i->second); + else + sutures->insert(make_pair(l, r)); } void -tree_state::apply_sutures() +tree_state::apply_sutures() const { for (std::map::const_iterator i = sutures->begin(); i != sutures->end(); ++i) @@ -1014,7 +1023,8 @@ { if (k == states->end()) { - states->insert(make_pair(i->second, j->second)); + item_status x(idx(*items, i->second)); + states->insert(make_pair(i->second, x.suture(j->second))); states->erase(j); } else @@ -1167,7 +1177,8 @@ i != trees.end() && x != changes.end(); ++i, ++x) { premaps.push_back(std::map()); - std::vector > curr = i->current(); + std::vector > + curr = i->current_with_dirs(); for (std::vector >::const_iterator j = curr.begin(); j != curr.end(); ++j) { @@ -1312,6 +1323,7 @@ if (trees.size() == 1) out.states.reset(new map(*out.states)); I(out.states != idx(trees, 0).states); + out.apply_sutures(); return out; } @@ -1346,8 +1358,8 @@ std::vector tree_state::conflict(tree_state const & other) const { + apply_sutures(); tree_state merged(mash(other)); - merged.apply_sutures(); std::vector out; std::map > m; @@ -1374,6 +1386,13 @@ I(right.size() == 1); c.lnames.push_back(get_full_name(*left.begin())); c.rnames.push_back(other.get_full_name(*right.begin())); + P(F("split: '%1%' vs '%2%' ('%3%' vs '%4%' (dirs are %5% vs %6%))") + % get_full_name(*left.begin()) + % other.get_full_name(*right.begin()) + % merged.get_full_name(*left.begin()) + % merged.get_full_name(*right.begin()) + % left.begin()->first + % right.begin()->first); out.push_back(c); } for (std::set::const_iterator @@ -1436,10 +1455,13 @@ std::vector > tree_state::current() const { + apply_sutures(); std::vector > out; for (std::map::const_iterator i = states->begin(); i != states->end(); ++i) { + if (i->second.is_dir) + continue; std::set s = i->second.current_names(); I(s.size() == 1); file_path fp = get_full_name(*s.begin()); @@ -1449,11 +1471,30 @@ return out; } +std::vector > +tree_state::current_with_dirs() const +{ + apply_sutures(); + std::vector > out; + for (std::map::const_iterator i = states->begin(); + i != states->end(); ++i) + { + std::set s = i->second.current_names(); + I(s.size() == 1); + file_path fp = get_full_name(*s.begin()); + if (!(fp == file_path())) + out.push_back(make_pair(i->first, fp)); + } + return out; +} + void tree_state::get_changes_for_merge(tree_state const & merged, change_set::path_rearrangement & changes) const { + this->apply_sutures(); + merged.apply_sutures(); changes.deleted_dirs.clear(); changes.deleted_files.clear(); changes.renamed_dirs.clear(); @@ -1464,48 +1505,76 @@ map::const_iterator l, r; l = states->begin(); r = merged.states->begin(); + item_status::item_state empty(-1, make_null_component()); while (l != states->end() || r != merged.states->end()) { file_path from, to; + std::set pres, posts; + item_status::item_state pre(empty), post(empty); bool from_is_dir(false), to_is_dir(false); if (l == states->end()) { - to = merged.get_full_name(r->second); + posts = r->second.current_names(); + I(posts.size() == 1); + post = *posts.begin(); + if (post != empty) + to = merged.get_full_name(post); to_is_dir = r->second.is_dir; ++r; } else if (r == merged.states->end()) { - from = get_full_name(l->second); + pres = l->second.current_names(); + I(pres.size() == 1); + pre = *pres.begin(); + if (pre != empty) + from = get_full_name(pre); from_is_dir = l->second.is_dir; ++l; } else if (l->first > r->first) { - to = merged.get_full_name(r->second); + posts = r->second.current_names(); + I(posts.size() == 1); + post = *posts.begin(); + if (post != empty) + to = merged.get_full_name(post); to_is_dir = r->second.is_dir; ++r; } else if (r->first > l->first) { - from = get_full_name(l->second); + pres = l->second.current_names(); + I(pres.size() == 1); + pre = *pres.begin(); + if (pre != empty) + from = get_full_name(pre); from_is_dir = l->second.is_dir; ++l; } else { - from = get_full_name(l->second); + posts = r->second.current_names(); + I(posts.size() == 1); + post = *posts.begin(); + if (post != empty) + to = merged.get_full_name(post); + pres = l->second.current_names(); + I(pres.size() == 1); + pre = *pres.begin(); + if (pre != empty) + from = get_full_name(pre); from_is_dir = l->second.is_dir; - to = merged.get_full_name(r->second); to_is_dir = r->second.is_dir; ++l, ++r; } - if (to == from) + if (pre == post) continue; else if (to == file_path()) { + I(!(from == file_path())); if (from_is_dir) changes.deleted_dirs.insert(from); else @@ -1513,6 +1582,7 @@ } else if (from == file_path()) { + I(!(to == file_path())); if (to_is_dir) ; else @@ -1520,6 +1590,7 @@ } else { + I(!(from == file_path())); if (from_is_dir) changes.renamed_dirs.insert(make_pair(from, to)); else @@ -1552,7 +1623,8 @@ } typedef int fpid; interner cit; - map names; + std::map names; + typedef std::pair::iterator, bool> nir; int lastlevel = 0; for (std::multimap >::const_iterator i = sorted.begin(); i != sorted.end(); ++i) @@ -1576,7 +1648,12 @@ continue;// not reached, or not resolved resolved.insert(j->first); fpid f(cit.intern(fp())); - names.insert(make_pair(f, j->first)); + nir r = names.insert(make_pair(f, j->first)); + if (r.first->second != j->first) + { + W(F("Suturing files %1%") % fp); + merged.add_suture(r.first->second, j->first); + } } } ++lastlevel; @@ -1620,9 +1697,34 @@ I(j != merged.states->end()); j->second = item_status(j->second.rename(merged.itx->intern(revision), k->second, name)); - cit.intern(fp()); + fpid f(cit.intern(fp())); + nir r = names.insert(make_pair(f, j->first)); + if (r.first->second != j->first) + { + W(F("Suturing files %1%") % fp); + merged.add_suture(r.first->second, j->first); + } } } + // nearly a c&p from if (i->first > lastlevel) + for (std::map::const_iterator + j = merged.states->begin(); j != merged.states->end(); ++j) + { + if (resolved.find(j->first) == resolved.end()) + { + file_path fp(merged.get_full_name(j->second)); + resolved.insert(j->first); + fpid f(cit.intern(fp())); + nir r = names.insert(make_pair(f, j->first)); + if (r.first->second != j->first) + { + W(F("Suturing items %1% and %2% over %3%") + % r.first->second % j->first % fp); + merged.add_suture(r.first->second, j->first); + } + } + } + merged.apply_sutures(); return merged; } ======================================================================== --- pcdv.hh 42ce2c9da4c7a1e77b1200885b6d3fc579592c69 +++ pcdv.hh 7ba58f71a35e6f8e212bc926d0ec2f15d00c2dd6 @@ -254,7 +254,7 @@ tree_state new_skel() const; void add_suture(item_id l, item_id r); - void apply_sutures(); + void apply_sutures() const; public: ~tree_state(); @@ -276,6 +276,8 @@ std::vector > current() const; + std::vector > + current_with_dirs() const; // get the changes along edge this->merged for merged=merge(revs) // note that revs should include *this ======================================================================== --- sanity.cc 36ad241e17f12b3d4e08f78ba1f302bb76523a76 +++ sanity.cc ffb40b74ba38772ebf90bf7bc0f7ac710933a338 @@ -253,19 +253,21 @@ for (std::vector::const_iterator i = musings.begin(); i != musings.end(); ++i) { + std::string tmp; try { - std::string tmp; (*i)->gasp(tmp); out << tmp; } catch (logic_error) { + out << tmp; out << "\n"; L(F("ignoring error trigged by saving work set to debug log")); } catch (informative_failure) { + out << tmp; out << "\n"; L(F("ignoring error trigged by saving work set to debug log")); } ======================================================================== --- tests/t_merge_add_del.at 20f850e8ede0f7c4240c1f9cb21855f151f89df2 +++ tests/t_merge_add_del.at 7abec2ef42ef3e1ae12434eb8fea72855ae3781e @@ -1,10 +1,7 @@ # -*- Autoconf -*- AT_SETUP([(imp) merging with ]) -# This test is a bug report. -AT_XFAIL_IF(true) - MONOTONE_SETUP # We want a graph which looks like: ======================================================================== --- tests/t_rename_added_in_rename.at 986d6f8c484b4cd73df3e6df7c67ad61f3bd1a2d +++ tests/t_rename_added_in_rename.at c73d2f1b0a4ba14c9165e2775c5e995a656c951d @@ -14,7 +14,7 @@ # | \ | # | merge 1 # | | -# RENAME_FILE_R | - rename_file foo/a bar/a +# RENAME_FILE_R | - rename_file foo/a foo/b # \ | # \ | # TEST_R