# # # patch "NEWS" # from [20cc058bff304df18e778f57a1c4676610bf2534] # to [a5c303d1604b8e394608ee7a8fdfd03e774a02c3] # # patch "database.cc" # from [25a7d09b691fa29743249d4151dc27bab18c7923] # to [5a61650edb2b4f71f83c39f327062de5ed827485] # # patch "database_check.cc" # from [eb2f10d33d8a4b345982a09ccb29a3d4235c06ab] # to [beb0e60421b8bf7552071a9af31ef6630cfd3b51] # # patch "tests/db_check_and_non-serious_errors/__driver__.lua" # from [489d591cb8383b6cfb17c02be4dc774cfa1e63cc] # to [908eadb2fc3c9e4ea028ff5b1e2e27bf01821406] # ============================================================ --- NEWS 20cc058bff304df18e778f57a1c4676610bf2534 +++ NEWS a5c303d1604b8e394608ee7a8fdfd03e774a02c3 @@ -79,6 +79,14 @@ xxx xxx xx xx:xx:xx UTC 2010 interrupted (^C). This was broken in 0.47 when it was fixed to not throw an exception on being interrupted. + - In 0.46 and 0.47, monotone could sometimes get confused about which + revisions were the heads of a particular branch. This would happen + when a new branch cert was added to a revision that was an ancestor + of one or more of the current heads of the branch, most commonly + during netsync when multiple people had performed identical merges. + This is fixed now. If your database currently gives incorrect 'heads' + results, you can fix it by either running 'mtn db regenerate_caches'. + Other - Support for the diffuse merger (http://diffuse.sourceforge.net) ============================================================ --- database.cc 25a7d09b691fa29743249d4151dc27bab18c7923 +++ database.cc 5a61650edb2b4f71f83c39f327062de5ed827485 @@ -3607,6 +3607,7 @@ database::record_as_branch_leaf(cert_val return; // already recorded (must be adding a second branch cert) bool all_parents_were_leaves = true; + bool some_ancestor_was_leaf = false; for (set::const_iterator p = parents.begin(); p != parents.end(); ++p) { @@ -3615,6 +3616,7 @@ database::record_as_branch_leaf(cert_val all_parents_were_leaves = false; else { + some_ancestor_was_leaf = true; imp->execute(query("DELETE FROM branch_leaves " "WHERE branch = ? AND revision_id = ?") % blob(branch()) % blob(l->inner()())); @@ -3637,6 +3639,7 @@ database::record_as_branch_leaf(cert_val { if (is_a_ancestor_of_b(*r, rev)) { + some_ancestor_was_leaf = true; imp->execute(query("DELETE FROM branch_leaves " "WHERE branch = ? AND revision_id = ?") % blob(branch()) % blob(r->inner()())); @@ -3644,6 +3647,23 @@ database::record_as_branch_leaf(cert_val } } + // are we really a leaf (ie, not an ancestor of an existing leaf)? + if (!some_ancestor_was_leaf) + { + bool really_a_leaf = true; + for (set::const_iterator r = current_leaves.begin(); + r != current_leaves.end(); ++r) + { + if (is_a_ancestor_of_b(rev, *r)) + { + really_a_leaf = false; + break; + } + } + if (!really_a_leaf) + return; + } + imp->execute(query("INSERT INTO branch_leaves(branch, revision_id) " "VALUES (?, ?)") % blob(branch()) % blob(rev.inner()())); ============================================================ --- database_check.cc eb2f10d33d8a4b345982a09ccb29a3d4235c06ab +++ database_check.cc beb0e60421b8bf7552071a9af31ef6630cfd3b51 @@ -641,7 +641,19 @@ check_branch_leaves(database & db, map computed_leaves; db.get_branch_leaves(i->value, cached_leaves); - db.compute_branch_leaves(i->value, computed_leaves); + try + { + db.compute_branch_leaves(i->value, computed_leaves); + } + catch (std::exception & e) + { + if (string(e.what()).find("height") != string::npos) + { + L(FL("error loading height when checking heads of '%s'") % i->value); + } + else + throw; + } checked_branches[name].heads_ok = cached_leaves == computed_leaves; ++ticks; ============================================================ --- tests/db_check_and_non-serious_errors/__driver__.lua 489d591cb8383b6cfb17c02be4dc774cfa1e63cc +++ tests/db_check_and_non-serious_errors/__driver__.lua 908eadb2fc3c9e4ea028ff5b1e2e27bf01821406 @@ -23,6 +23,10 @@ end local str = string.format("delete from %s where %s = x'%s'", a, b, del_rev) check(mtn("db", "execute", str), 0, false, false) end +check(mtn("db", "execute", + string.format("update branch_leaves set revision_id = x'%s' where revision_id = x'%s'", + rev, del_rev)), + 0, false, false) -- and also a few unused files shall float about check(mtn("fload"), 0, false, false, {"fileX"})